栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Spring

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Spring

目录

1.Spring概述

1.1.什么是Spring

EE开发分成三层结构

1.2.为什么学习Spring

1.3.http://localhost:1111/spring-basic.html - spring的版本Spring的版本

1.4.spring功能模块划分

 2、JAVA类的耦合与解耦

2.1.什么是程序的耦合

2.2.解决耦合的思路

2.2.1.工厂模式解耦

2.2.2.控制反转-Inversion Of Control

3.Spring的 IOC

3.1.什么是IoC

3.2.IoC的作用

3.3.基于XML的IoC配置入门

3.4.ApplicationContext

4. Bean的配置

4.1.spring中的bean元素相关配置Spring中的bean元素相关配置

4.1.1.id属性和name属性

4.1.2.scope属性

4.1.3.生命周期属性

4.2.Spring生成Bean的时候的三种方式

4.3.Spring中Bean属性的注入

4.3.1.方式1:构造方法的方式注入属性

4.3.2.方式2:set方法的方式注入属性

4.4.在配置文件中引入Properties配置

4.5.复杂类型的注入

​编辑4.6.Spring的分配置文件开发

5.Spring注解方式的Bean管理

5.1.Spring Bean管理中的常用注解

5.1.1.准备工作

5.1.2.@Component

5.1.3.属性注入的注解@Value、@Autowired

5.1.4.Bean的作用范围的注解

5.1.5.Bean的生命周期的配置

6.AOP的概述

6.1.什么是AOP

6.2.为什么学习AOP

6.3.Spring的AOP的由来

6.4.底层实现

7.Spring AOP基于XML实现

7.1.AOP开发中的相关术语

7.2.基于XML配置方式声明切面

7.2.1.pom依赖

7.2.2.写切面类

7.2.3.被代理的业务类

7.2.4.配置aop切面

8.Spring AOP基于Annotation实现

8.1.基于注解方式声明切面

8.1.1.pom依赖

8.1.2.启动@Aspect注解的支持和注解的扫描机制

8.1.3.写切面类

8.1.4.写一个业务类(被代理的类)

8.1.5.测试类


1.Spring概述

1.1.什么是Spring

spring (由Rod Johnson创建的一个开源框架)
Spring是一个开放源代码的设计层面框架,他解决的是业务逻辑层和其他各层的松耦合问题,因此它将面向接口的编程思想贯穿整个系统应用。Spring是于2003年兴起的-一个轻量级的Java开发框架,由Rod Johnson创建。简单来说,Spring是一个分。层的JavaSE/EE full-stack(- 站式)轻量级开源框架。

Spring是一个开源框架,Spring是于2003年兴起的一个轻量级的Java开发框架,由Rod Johnson在其著作 Expert One-On-One J2EE Development and Design 中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为J2EE应用程序开发提供集成的框架。Spring使用基本的 JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益。 Spring的 核心是控制反转(IoC)和面向切面(AOP) 。简单来说, Spring是一个分层的JavaSE/EEfull-stack(一站式)轻量级开源框架。

EE开发分成三层结构
  • WEB层: Spring MVC
  • 业务层: Bean管理:(IOC)
  • 持久层: Spring的JDBC模板。ORM模板用于整合其他的持久层框架

Expert One-to-One J2EE Design and Development : J2EE 的设计和开发:(2002.EJB)
Expert One-to-One J2EE Development without EJB : J2EE 不使用EJB的开发

1.2.为什么学习Spring

1、方便解耦,简化开发
        Spring就是一个大工厂,可以将所有对象创建和依赖关系维护,交给Spring管理
2、AOP编程的支持
        Spring提供面向切面编程,可以方便的实现对程序进行权限拦截、运行监控等功能
3、声明式事务的支持
        只需要通过配置就可以完成对事务的管理,而无需手动编程
4、方便程序的测试
        Spring对Junit4支持,可以通过注解方便的测试Spring程序
5、方便集成各种优秀框架
        Spring不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如:Struts、Hibernate、MyBatis、Quartz等)的直接支持
6、降低JavaEE API的使用难度
        Spring对JavaEE开发中非常难用的一些API(如:JDBC、JavaMail、远程调用等),都提供了封装,使这些API应用难度大大降低

1.3.http://localhost:1111/spring-basic.html - spring的版本Spring的版本

Spring3.X 和 Spring4.X 和 Spring5.X

1.4.spring功能模块划分

 2、JAVA类的耦合与解耦

2.1.什么是程序的耦合

在开发中可能会写很多的类,而有些类之间不可避免的产生依赖关系,这种依赖关系称之为耦合。
有些依赖关系是必须的,有些依赖关系可以通过优化代码来解除的。

public class CustomerServiceImpl implements CustomerService {
    CustomerDao customerDao =new CustomerDaoImpl();
}

以上的代码表示:业务层调用持久层,并且此时业务层在依赖持久层的接口和实现类。如果此时没有持久层实现类,编译将不能通过。这种依赖关系就是我们可以通过优化代码解决的。 
还有如下面的代码:
我们的类依赖了MySQL的具体驱动类,如果这时候因为某些原因数据库的品牌从MySQL改为Oracle,那么需要通过改源码来修改数据库驱动。这显然不是我们想要的。

public class JdbcDemo01{
    public static void main(String[] args) throws Exception {
        Class.forName("com.mysql.jdbc.Driver");
        //new com.mysql.jdbc.Driver()
    }
}

2.2.解决耦合的思路

当是我们学习JDBC时,是通过反射来注册驱动的,代码如下:
Class.forName("com.mysql.jdbc.Driver");
这时的好处是,我们的类中不再依赖具体的驱动类,此时就算删除mysql的驱动jar包,依然可以编译。但是因为没有驱动类,所以不能运行。
不过,此处也有个问题,就是我们反射类对象的全限定类名字符串是在java类中写死的,一旦要改还是要修改源码。
解决这个问题也很简单,使用配置文件配置。

2.2.1.工厂模式解耦

在实际开发中我们可以把所有的dao和service和controller对象使用配置文件配置起来,当启动服务器应用加载的时候,通过读取配置文件,把这些对象创建出来并存起来。在接下来的使用的时候,直接拿过来用就好了。

2.2.2.控制反转-Inversion Of Control

上面解耦的思路有2个问题:
1、存哪去?
        分析:由于我们是很多对象,肯定要找个集合来存。这时候有Map和List供选择。
        到底选Map还是List就看我们有没有查找需求。有查找需求,选Map。
        在应用加载时,创建一个Map,用于存放controller,Service和dao对象。
        我们把这个map称之为容器。
2、还是没解释什么是工厂?
        工厂就是负责给我们从容器中获取指定对象的类。这时候我们获取对象的方式发生了改变。
        原来:我们在获取对象时,都是采用new的方式。是主动的。

        现在:我们获取对象时,同时跟工厂要,有工厂为我们查找或者创建对象。是被动的。

        这种被动接收的方式获取对象的思想就是控制反转,它是spring框架的核心之一。
        它的作用只有一个:削减计算机程序的耦合。

3.Spring的 IOC

3.1.什么是IoC

Inversion of Control:控制权的转移,创建对象的权利由应用程序转移到容器称为控制反转

3.2.IoC的作用

削减计算机程序的耦合(解除我们代码中的依赖关系)

3.3.基于XML的IoC配置入门

创建Maven项目,resources 创建spring配置文件

 pom.xml依赖


    org.springframework
    spring-context
    5.3.8

编写UserService类

public class UserService {
    public UserService() {
        System.out.println("调用了UserService的构造方法");
    }

    public void add() {
        System.out.println("调用了UserService的add方法");
    }

    public void init() {
        System.out.println("调用了UserService的init方法");
    }

    public void destroy() {
        System.out.println("调用了UserService的destroy方法");
    }
}

编写applicationContext.xml配置文件



    
    
    
    
    

编写测试类Test

package com.tjetc;

import com.tjetc.service.UserService;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
    public static void main(String[] args) {
        //创建ClassPathXmlApplicationContext对象, 参数就是定义spring的配置文件
        //new ClassPathXmlApplicationContext成功之后,UserService配置的类已经被实例化了
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        //通过context的getBean方法获取UserService对象
        UserService userService = context.getBean(UserService.class);
        //通过配置的id名称获取UserService对象
        UserService userService1 = (UserService) context.getBean("userService");
        // UserService userService1 =(UserService) context.getBean("aaa");
        //调用UserService的add方法
        userService.add();
        userService1.add();
        System.out.println(userService == userService1);//true
    }
}

结果:

3.4.ApplicationContext

ApplicatioContext接口有两个基本的实现类:

ClassPathXmlApplicationContext : 加载类路径下的Spring配置文件。
FileSystemXmlApplicationContext : 加载本地磁盘下的Spring配置文件。

4. Bean的配置

4.1.spring中的bean元素相关配置Spring中的bean元素相关配置

配置文件中的bean元素用于描述需要Spring容器管理的对象。其 class属性用于指被管理对象的完整类名。

4.1.1.id属性和name属性

id属性 

是为Spring容器管理的对象起个名字。其使用ID约束:

  • 唯一
  • 必须以字母开始
  • 可以使用字母、数字、连字符、下划线、句号、冒号。但 不能出现特殊字符

name属性 
也是为被Spring容器管理的对象起个名字。这样后续可以通过该名字从容器中获取对象。
没有ID中的那些约束(可以重复[不推荐]、可以出现特殊字符)
如果没有id的话,name可以当做id使用
举例


 

 此处的名称中由于有特殊字符,只能使用name属性。
结论:在定义bean的时候,推荐使用name属性来为bean指定名称

4.1.2.scope属性

用于声明bean在Spring容器中的作用范围。其取值包括:
singleton : 默认值  单例的;默认在Spring容器启动的时候就会创建该实例。
prototype : 多例的;默认在从Spring容器中获取bean时才会创建该实例。
request : WEB项目中,Spring创建一个Bean的对象,将对象存入到request域中。
session : WEB项目中,Spring创建一个Bean的对象,将对象存入到session域中。
globalSession : WEB项目中,应用在Porlet环境。如果没有Porlet环境,那么globalSession相当于session。

singleton案例

applicationContext.xml




    
    

测试类:

import com.tjetc.service.UserService;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestTest {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        //的配置项 scope="prototype" context.getBean时候创建对象,每次getBean都是新对象
        UserService userService1 = context.getBean(UserService.class);
        UserService userService2 = context.getBean(UserService.class);
        //==判断两个对象的地址是否相等
        System.out.println(userService1 == userService2);
    }
}

prototype案例

测试类没有改变,只是修改application.xml的配置,运行结果也会有不同



 

4.1.3.生命周期属性

在bean被创建的时候,需要执行一些初始化的逻辑。可以指定bean中的一个方法为其初始化方法。这样Spring在创建完该对象之后,立即调用一下该初始化方法。

在bean对象销毁的时候,需要执行一些销毁逻辑。可以指定bean中的一个方法为其销毁方法。这样Spring容器在关闭之前并且销毁该对象的时候,会调用一下该销毁方法。

注意,此处的销毁方法必须是在容器正常关闭(即执行close方法)时,才会被执行。

init和destroy案例

applicationContext.xml




    
    
    

测试类:

import com.tjetc.service.UserService;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestTest {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService = context.getBean(UserService.class);
        userService.add();
        //正常关闭
        context.close();
    }
}

 

4.2.Spring生成Bean的时候的三种方式

方式1:无参数的构造方法的方式

方式2:静态工厂实例化的方式

import com.tjetc.service.UserService;

public class UserServiceFactory {
    public static UserService getBean() {
        //这里可以写很复杂的逻辑, 模拟业务
        return new UserService();
    }
}

测试类:

import com.tjetc.service.UserService;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test12 {
    public static void main(String[] args) {
            //创建Spring的上下文对象[调用了UserService的构造方法]
            ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
            //通过id值获取bean对象然后强转为UserService
            UserService userService2 = (UserService) context.getBean("userService2");
            //[调用了UserService的add方法]
            userService2.add();
        }
}

 

方式3:实例工厂实例化的方式

import com.tjetc.service.UserService;

public class UserServiceFactory2 {
    
    public UserService getBean2() {
        return new UserService();
    }
}




测试类:

import com.tjetc.factory.UserServiceFactory2;
import com.tjetc.service.UserService;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test12 {
    public static void main(String[] args) {
        //创建Spring的上下文对象[调用了两次UserService的构造方法, 因为配置了两次,id="userService2",id="userService3"]
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        //获取工厂bean
        UserServiceFactory2 userServiceFactory2 = (UserServiceFactory2) context.getBean("userServiceFactory2");
        System.out.println(userServiceFactory2);
        //获取UserServiceBean
        UserService userService3 = (UserService) context.getBean("userService3");
        userService3.add();
        UserService userService33 = (UserService) context.getBean("userService3");
        //true,bean 单例 在配置,getBean多次也是一个对象
        System.out.println(userService3 == userService33);
        context.close();
    }
}

4.3.Spring中Bean属性的注入

4.3.1.方式1:构造方法的方式注入属性

定义Car

public class Car {
    private String name;
    private int price;

    
    public Car() {
    }

    public Car(String name, int price) {
        this.name = name;
        this.price = price;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Car{" +
                "name='" + name + ''' +
                ", price=" + price +
                '}';
    }
}

Spring配置Car

    
    
        
        
        
    

测试

import com.tjetc.entity.Car;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test12 {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        //通过类型获取bean
        Car car = context.getBean(Car.class);
        System.out.println(car);
    }
}

 

4.3.2.方式2:set方法的方式注入属性

基本类型的属性注入

Spring配置

    
    
    
        
        
        
    

测试

import com.tjetc.entity.Car;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test12 {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext1.xml");
        Car car1 = (Car) context.getBean("car1");
        System.out.println(car1);
    }
}

 

对象类型的属性注入
定义Person

package com.tjetc.entity;

public class Person {
    private String name;
    private Car car;//自定义的Car类型

    public Person() {
    }

    public Person(String name, Car car) {
        this.name = name;
        this.car = car;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Car getCar() {
        return car;
    }

    public void setCar(Car car) {
        this.car = car;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + ''' +
                ", car=" + car +
                '}';
    }
}

Spring配置

    
    
        
        
        
    

测试:

import com.tjetc.entity.Person;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test12 {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext1.xml");
        Person person = (Person) context.getBean("person");
        System.out.println(person);
    }
}

4.4.在配置文件中引入Properties配置

db.properties

jdbc.driverName=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/db2?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
jdbc.username=root
jdbc.password=123456

方案1:配置Spring的Bean PropertySourcesPlaceholderConfigurer

        
        
            
                
                    classpath*:db.properties
                
            
        

    
    
        
        
        
        
        
    

方案2:引入 context名称空间 。使用context中提供的  指定它的 location 属性值,如果有多个文件,使用 逗号 分割。




    
        

    
    
        
        
        
        
        
    
public class Test12 {
    public static void main(String[] args) {
//        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext1.xml,applicationContext.xml");
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext1.xml");
        DbConfiguration dbConfiguration = (DbConfiguration) context.getBean("dbConfiguration");
        System.out.println(dbConfiguration);
    }
}

 classpath:xx 这种写法,只会搜索到文件夹类型的下面的资源。不会搜索到jar包中的。 classpath*:xx 这种写法,都会搜索到。所以【推荐用这种】

4.5.复杂类型的注入

定义CollectionDemo

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Properties;

public class CollectionDemo {
    private String[] strs;
    private List list;
    private Map map;
    private Properties properties;

    public String[] getStrs() {
        return strs;
    }

    public void setStrs(String[] strs) {
        this.strs = strs;
    }

    public List getList() {
        return list;
    }

    public void setList(List list) {
        this.list = list;
    }

    public Map getMap() {
        return map;
    }

    public void setMap(Map map) {
        this.map = map;
    }

    public Properties getProperties() {
        return properties;
    }

    public void setProperties(Properties properties) {
        this.properties = properties;
    }

    @Override
    public String toString() {
        return "CollectionDemo{" +
                "strs=" + Arrays.toString(strs) +
                ", list=" + list +
                ", map=" + map +
                ", properties=" + properties +
                '}';
    }
}

Spring配置

    
        
        
            
                张三
                李四
            
        

        
        
            
                123
                456
            
        

        
        
            
                
                
            
        

        
        
            
                肖
                王
            
        

    

测试

import com.tjetc.entity.CollectionDemo;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test12 {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext1.xml");
        CollectionDemo collectionDemo = (CollectionDemo) context.getBean("collectionDemo");
        System.out.println(collectionDemo);
    }
}


4.6.Spring的分配置文件开发

随着项目越来越大,Spring的配置文件的内容也会越来越多。在实际的开发中,会将不同的配置定义在不同的xml文件中。有两种使用方式:
        方式1:在创建容器的时候加载多个配置文件
        ApplicationContext applicationContext = new                 ClassPathXmlApplicationContext("applicationContext.xml","propertiesContext.xml");
        方式2:在主配置文件中包含其它配置文件(推荐使用)
        

        

propertiesContext.xml




    
    
        
            
                classpath*:db.properties
            
        
    

    
    

applicationContext.xml

    
    

    
    
        
        
        
        
        
    

test 

import com.tjetc.common.DbConfiguration;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test12 {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext1.xml");
        DbConfiguration dbConfiguration = (DbConfiguration) context.getBean("dbConfiguration");
        System.out.println(dbConfiguration);
    }
}

5.Spring注解方式的Bean管理

主要解决的问题,在XML中一个一个对bean进行配置,开发效率也不高。
目的:

  • 自动发现哪些对象需要让Spring进行管理,自动将其注册到Spring容器中。
  • 自动注入bean依赖的内容。

5.1.Spring Bean管理中的常用注解

5.1.1.准备工作

使用注解要先开启注解扫描的功能


5.1.2.@Component

标注在类上,说明这是一个Spring组件。Spring中目前还提供了与 @Component 功能一致的其它三个衍生注解:

  1. @Controller :WEB 层(控制层)
  2. @Service :业务层
  3. @Repository :持久层

        衍生的三个注解是为了让标注类本身的用途清晰,Spring在后续版本会对其增强
作用和XML配置文件的标签的实现功能一致;

5.1.3.属性注入的注解@Value、@Autowired

使用注解注入的方式,可以不用提供set方法。
相关注解
@Value : 用于注入普通类型和String类型。
        加在成员变量上——原理是反射赋值(破坏封装)
        加在set方法上——原理是方法调用(推荐使用)
        它可以使用spring中的spEL(也就是spring的el表达式),SpEl的写法:${表达式}
@Value使用

package com.tjetc.common;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component("adminConstant")//小括号字符串是AdminConstant类的bean的名称
public class AdminConstant {
    //@Value可以把配置数据注入到成员变量或者set×××方法, 前提条件:Spring要对类实例化并管理
    //@value 通过${}  读取key对应的value数据
    @Value("${img.base.path}")
    private String basePath;
    private String baseType;
    @Value("111111")
    private String abc;

    @Value("${img.base.type}")
    public void setBaseType(String baseType) {
        this.baseType = baseType;
    }

    public String getBasePath() {
        return basePath;
    }

    public void setBasePath(String basePath) {
        this.basePath = basePath;
    }

    public String getBaseType() {
        return baseType;
    }

    @Override
    public String toString() {
        return "AdminConstant{" +
                "basePath='" + basePath + ''' +
                ", baseType='" + baseType + ''' +
                ", abc='" + abc + ''' +
                '}';
    }
}

applicationContext.xml配置



admin.properties

img.base.path=c:/image
img.base.type=jpg,jpeg,gif,png

测试

package com.tjetc;

import com.tjetc.common.AdminConstant;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test12 {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext2.xml");
        AdminConstant adminConstant = (AdminConstant) context.getBean("adminConstant");
        System.out.println(adminConstant);
    }
}


@Autowired : 用于注入对象类型(自动装配)
        默认按类型进行装配
        按名称注入: @Qualifier——强制使用名称注入,一般不能单独使用和Autowired配合使用,当注入的对象类型有多个时,@Autowoired必须与@Qualifier一起使用

@Resource : 相当于@Autowired和@Qualifier一起使用

 UserController.java

package com.tjetc.controller;

import com.tjetc.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;

import javax.annotation.Resource;

@Controller
public class UserController {
    // @Autowired 按照类型从spring容器中查找到UserService的bean, 注入到成员变量
    // 依赖查找: 根据类型或者bean的名称, 在spring容器中查找相应类型的bean
    // 依赖注入: 根据依赖查找到的bean对象, 赋值到指定的成员变量或者set方法
    // 按照类型注入存在一个问题: spring容器里有很多个类型相同, 但不是同一个对象的bean, 按照类型注入会报错
//     @Autowired
    // @Qualifier  ----强制使用名称注入, 一般不能单独使用, 要和Autowired配合使用
//     @Qualifier("userService1")

//    @Resource = @Autowired + Qualifier
    @Resource(name = "userService1")//依赖查找并注入,会拿到新的,打印构造方法和初始化
//    @Resource(name = "userService2")//单例,  不会打印出init, 单例体现在getBean(),不体现在配置上
    private UserService userService;

    
    public void add() {
        
        userService.add();
    }
}

 

applicationContext.xml配置


5.1.4.Bean的作用范围的注解

注解@Scope

  • singleton : 单例
  • prototype : 多例

5.1.5.Bean的生命周期的配置

相关注解

  • @PostConstruct : 相当于 init-method
  • @PreDestroy : 相当于 destroy-method

测试

package com.tjetc.service;

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

@Service("userService1")
//@Scope("singleton")//单例
@Scope("prototype")//多利

public class UserService {
    public UserService() {
        System.out.println("调用了UserService的构造方法");
    }

    public void add() {
        System.out.println("调用了UserService的add方法");
    }

    //@PostConstruct  相当于init-method
    @PostConstruct
    public void init() {
        System.out.println("调用了UserService的init方法");
    }

    // @PreDestroy     相当于destroy-method
    @PreDestroy//要关闭context.close();
    public void destroy() {
        System.out.println("调用了UserService的destroy方法");
    }
}
package com.tjetc;

import com.tjetc.common.AdminConstant;
import com.tjetc.controller.UserController;
import com.tjetc.service.UserService;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test12 {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext2.xml");
        UserService userService1 =(UserService) context.getBean("userService1");
        UserService userService2 =(UserService) context.getBean("userService1");
        //判断地址是否相等,不等,说明是两个对象,相等说明是一个对象
        System.out.println(userService1==userService2);//不加多例为true,加了多例为false
        context.close();
    }
}

6.AOP的概述

Aop思想

6.1.什么是AOP

AOP(面向切面编程)
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统-维护的-种技术。AOP是OOP的延续,是软件开发中的-个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

Spring是解决实际开发中的一些问题:

  • AOP解决OOP中遇到的一些问题。是OOP的延续和扩展。
  • Spring引入AOP思想来为容器中管理的对象动态生成代理对象。

6.2.为什么学习AOP

AOP的常用应用场景

  • 权限校验
  • 日志记录
  • 性能监控
  • 事务控制

6.3.Spring的AOP的由来

AOP最早由AOP联盟的组织提出的,制定了一套规范。Spring将AOP思想引入到框架中,必须遵守AOP联盟的规范。

6.4.底层实现

Spring的AOP底层用到两种代理机制
JDK 的动态代理 : 针对实现了接口的类产生代理。
        被代理的类必须要实现接口,才能产生代理对象。如果没有接口将不能使用动态代理技术。
Cglib 的动态代理 : 针对没有实现接口的类产生代理。应用的是底层的字节码增强的技术生成当前类的子类对象。
        可以对任何 非final声明的 类生成代理。

7.Spring AOP基于XML实现

7.1.AOP开发中的相关术语

Joinpoint(连接点) : 所谓连接点是指 那些被拦截到的点 。在Spring中,这些点指的是方法。
Pointcut(切入点) : 所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义。
Advice(通知/增强) : 所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知。通知分为前置通知、后置 通知、异常通知、最终通知、环绕通知(切面要完成的功能)。
Aspect(切面) : 是切入点和通知(引介)的结合,即切入点+通知

7.2.基于XML配置方式声明切面

7.2.1.pom依赖

    org.aspectj
    aspectjweaver
    1.9.7

7.2.2.写切面类
package com.tjetc.aop;

import com.tjetc.common.DbDruidUtils;
import org.aspectj.lang.ProceedingJoinPoint;


public class XMLTransactionPrint {
    public void doBefore() {
        //模拟业务
        System.out.println("前置通知");
//        System.out.println("开启事务");
    }

    public void doAfterReturning() {
        //模拟业务
        System.out.println("后置通知");
//        System.out.println("提交事务");
    }

    public void doAfterThrowing() {
        //模拟业务
        System.out.println("异常通知");
//        System.out.println("回滚事务");
    }

    public void doAfter() {
        //模拟业务
        System.out.println("最终通知");
//        System.out.println("关闭连接");
    }

    
    public Object doRound(ProceedingJoinPoint pjp) throws Throwable {
        try {
//            System.out.println("环绕前置通知");
            System.out.println("开启事务");
            DbDruidUtils.beginTransaction();
            //执行业务类的方法
            Object result = pjp.proceed();
//            System.out.println("环绕后置通知");
            System.out.println("提交事务");
            DbDruidUtils.commit();
            return result;
        } catch (Throwable throwable) {
            throwable.printStackTrace();
//            System.out.println("环绕异常通知");
            System.out.println("回滚事务");
            DbDruidUtils.rollback();
            throw throwable;
        } finally {
//            System.out.println("环绕最终通知");
            System.out.println("关闭连接");
            DbDruidUtils.closeConnection();
        }
    }
}

7.2.3.被代理的业务类
package com.tjetc.service;

import org.springframework.stereotype.Service;


@Service
public class PersonService {
    public void add() {
        System.out.println("调用了PersonService的add方法");
    }

    public void update() {
        //查询
        //更新库存  更新库存失败(没有出现异常),主动抛出异常
        //添加订单
        System.out.println("调用了PersonService的update方法");
    }

    public void delete() {
        System.out.println("调用了PersonService的delete方法");
        int i = 1 / 0;
    }
}

7.2.4.配置aop切面


    
    

    
    
        
        
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            


            


        
    

测试类:

package com.tjetc;

import com.tjetc.service.PersonService;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestAop {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContextAop.xml");
        PersonService personService = context.getBean(PersonService.class);
        personService.add();
        System.out.println("=========================");
        personService.update();
        System.out.println("=========================");
        personService.delete();
    }
}

配置通知运行

环绕通知运行

 

 开启环绕通知配置,注释前置通知、后置通知、异常通知、最终通知,进行测试

8.Spring AOP基于Annotation实现

8.1.基于注解方式声明切面

8.1.1.pom依赖

    org.aspectj
    aspectjweaver
    1.9.7

8.1.2.启动@Aspect注解的支持和注解的扫描机制



    
    
    
    

8.1.3.写切面类

@Aspect声明的类,和普通类一样可以添加属性和方法,还可以包含切入点、通知、引入。
 Pointcut声明: 切入点声明包含两个部分: 
 1.签名:由一个名字和多个参数组成,必须返回void, 如:private void anyMethod(){}
   2.切入点表达式  如:@Pointcut("execution(* com.tjetc.service..*.*(..))")

package com.tjetc.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;


@Component  //纳入Spring管理
@Aspect //切面类(增强类)
public class AnnotationTransactionPoint {
    //定义一个空方法, 要求返回值为void, 作为切入点表达式
    // @Pointcut  用来编写切点表达式
    @Pointcut("execution(* com.tjetc.service..*.*(..))")
    private void anyMethod() {

    }

    //编写四大通知
    //前置通知
//    @Before("anyMethod()")//@Before  注解的值  写切点表达式对应的方法名称+(),  引入切点表达式
    public void doBefore() {
        System.out.println("前置通知");
    }

//    @AfterReturning("anyMethod()")
    public void doAfterReturning() {
        System.out.println("后置通知");
    }

//    @AfterThrowing("anyMethod()")
    public void doAfterThrowing() {
        System.out.println("异常通知");
    }

//    @After("anyMethod()")
    public void doAfter() {
        System.out.println("最终通知");
    }

    @Around("anyMethod()")
    public Object doRound(ProceedingJoinPoint pjp) throws Throwable {
        try {
            System.out.println("环绕前置通知");
            //执行业务方法
            Object result = pjp.proceed();
            System.out.println("环绕后置通知");
            return result;
        } catch (Throwable throwable) {
            System.out.println("环绕异常通知");
            throwable.printStackTrace();
            throw throwable;
        } finally {
            System.out.println("环绕最终通知");
        }
    }
}

8.1.4.写一个业务类(被代理的类)
package com.tjetc.service;

import org.springframework.stereotype.Service;


@Service
public class PersonService {
    public void add() {
        System.out.println("调用了PersonService的add方法");
    }

    public void update() {
        //查询
        //更新库存  更新库存失败(没有出现异常),主动抛出异常
        //添加订单
        System.out.println("调用了PersonService的update方法");
    }

    public void delete() {
        System.out.println("调用了PersonService的delete方法");
        int i = 1 / 0;
    }
}

8.1.5.测试类
package com.tjetc;

import com.tjetc.service.PersonService;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestAopForAnnotation {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContextAopForAnnotation.xml");
        PersonService personService = context.getBean(PersonService.class);
        personService.add();
        System.out.println("=========================");
        personService.update();
        System.out.println("=========================");
        personService.delete();
    }
}

配置环绕

 环绕通知


开启环绕通知配置,注释前置通知、后置通知、异常通知、最终通知,进行测试

 

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/991737.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号