Loading

java.Spring5

IOC容器的原理

springnnx

IOC容器的入门

环境准备

导入依赖

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.5.RELEASE</version>
        </dependency>

创建配置:applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
 
</beans>

创建容器

第一种形式:

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");

第二种形式:

ApplicationContext applicationContext = new FileSystemXmlApplicationContext("C:/applicationContext.xml");

IOC容器的Bean管理(XML方法)

配置Bean的三种形式

<bean class="com.caochenlei.spring5.bean.User"></bean>

<bean id="user" class="com.caochenlei.spring5.bean.User"></bean>

<bean name="user" class="com.caochenlei.spring5.bean.User"></bean>

Bean取别名的三种方式

<bean class="com.caochenlei.spring5.bean.User"></bean>
<alias name="com.caochenlei.spring5.bean.User" alias="systemUser"></alias>

<bean id="user" class="com.caochenlei.spring5.bean.User"></bean>
<alias name="user" alias="systemUser"></alias>

<bean name="user" class="com.caochenlei.spring5.bean.User"></bean>
<alias name="user" alias="systemUser"></alias>    

获取Bean的三种形式(实例化)

ApplicationContext applicationContext1 = new ClassPathXmlApplicationContext("applicationContext.xml");
User user = applicationContext1.getBean(User.class);
System.out.println(user);

ApplicationContext applicationContext2 = new ClassPathXmlApplicationContext("applicationContext.xml");
User user = (User) applicationContext2.getBean("user");
System.out.println(user);


ApplicationContext applicationContext3 = new ClassPathXmlApplicationContext("applicationContext.xml");
User user = applicationContext3.getBean("user", User.class);
System.out.println(user);

Bean依赖注入

Bean的自动注入

Bean的实例化

Bean的前置实例化

Bean的延迟实例化

Bean的作用域

Bean的生命周期

IOC容器的Bean管理(注解方式)

半注解开发

image-20210521230300484

注解说明

注解 说明
@Component 使用在类上用于实例化Bean
@Controller 使用在web层类上用于实例化Bean
@Service 使用在service层类上用于实例化Bean
@Repository 使用在dao层类上用于实例化Bean
@Autowired 使用在字段上用于根据类型依赖注入
@Qualifier 结合@Autowried一起使用于根据名称进行依赖注入
@Resource 想当@Autowired + @Qualifier,按照名称进行注入
@Value 注入普通属性
@Scope 标注Bean的作用范围
@PostConstruct 使用在方法上标注该方法是Bean的初始化方法
@PreDestroy 使用在方法上标注该方法是Bean的销毁方法

导入依赖

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.2</version>
    </dependency>

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.20</version>
    </dependency>

    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13.1</version>
        <scope>test</scope>
    </dependency>
</dependencies>

applicationContext 命名空间

xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd

Bean的配置

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!--    扫描所有组件-->
    <context:component-scan base-package="com.ccl.bean"/>

    <!--    被代替的xml管理方式,并且它可以扫描到bean下的所有类-->
    <bean id="user" class="com.ccl.bean.User"></bean>

    <!--引入外部文件-->
    <context:property-placeholder location="classpath:admin.properties"/>

    <!--        按组件配置扫描-->
    <context:component-scan base-package="com.ccl.bean" use-default-filters="false">
        <!--        只扫描《注解类型》,注解类型为:@Component-->
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Component"/>

        <!--        不扫描《注解类型》,注解类型为:@Repository、@Service、@Controller-->
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>
</beans>

User

@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor

//普通类实例化Bean的注解,空参可以
@Component("user")

//其他用途的注解也可以实例化普通类
//@Repository
//@Service
//@Controller
public class User {
    private Integer id;
    private String name;
    private String gender;
}

测试

@Test
public void test(){
    ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
    User user = app.getBean("user", User.class);
    System.out.println(user);
}

Bean的依赖注入

UserDao

//Dao层类实例化Bean的注解
@Repository
public class UserDao {
    public User findOne(Integer id){
        return new User(01,"leesin","man");
    }
}

根据类型自动注入

第一种形式:将依赖注入私有属性中
//Service层类实例化Bean的注解
@Service
public class UserService {
    //相当于@Autowired + AQualifier,按照名称进行注入
    @Resource
    private UserDao userDao;

    public User userDao(Integer id) {
        return userDao.findOne(id);
    }
}
第二种形式:将依赖注入到setter方法中
//Service层类实例化Bean的注解
@Service
public class UserService {
    private UserDao userDao;

    //相当于@Autowired + AQualifier,按照名称进行注入
    @Resource
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public User userDao(Integer id) {
        return userDao.findOne(id);
    }
}
第三种形式:将依赖注入到有参构造方法中
//Service层类实例化Bean的注解
@Service
public class UserService {
    private UserDao userDao;

    //@Resource  不适用于构造方法
    //    @Resource  
    
    //使用在字段上根据类型依赖注入
    @Autowired
    public UserService(UserDao userDao){
        this.userDao = userDao;
    }

    public User userDao(Integer id) {
        return userDao.findOne(id);
    }
}
测试
@Test
public void testAnnotation() {
    ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
    UserService userService = app.getBean("userService", UserService.class);
    User one = userService.userDao(01);
    System.out.println(one);
}
@Resource
@Resource注解与@Autowired注解一样,都可以用来自动装配bean。但我们要知道,@Autowired注解是属于Spring的,而@Resource注解是Java规范。@Resource注解也可以用在字段或者setter方法上。
  1. 写在字段上

    1. @Resource
      private UserDao userDao;
      
    2. @Resource(name = "userDao")
      private UserDao userDao;
      

      注意事项:当@Resource注解写在字段上时,默认先使用名称进行匹配,名称可以通过@Resource的name属性指定,如上。当没有指定name属性的时候,则默认取字段名作为名称查找。当按名称找不到的时候,则按照字段的类型进行匹配。最后若还是没有找到,则报异常

      nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException
      
  2. 写在setter方法上

    private UserDao userDao;
    
    @Resource
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
    

根据名称自动注入

第一种形式:将依赖注入到私有属性中
//Service层类实例化Bean的注解
@Service
public class UserService {

    //根据类型依赖注入
    @Autowired
    //根据名称依赖注入
    @Qualifier("userDao")
    //相当于@Autowired + AQualifier,按照名称进行注入
    //@Resource(name = "userDao")
    private UserDao userDao;

    public User userDao(Integer id) {
        return userDao.findOne(id);
    }
}
第二种方式:将依赖注入到setter方法中
//Service层类实例化Bean的注解
@Service
public class UserService {
    private UserDao userDao;

    //根据类型依赖注入
    @Autowired
    //根据名称依赖注入
    @Qualifier("userDao")
    //相当于@Autowired + AQualifier,按照名称进行注入
    //@Resource(name = "userDao")
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public User userDao(Integer id) {
        return userDao.findOne(id);
    }
}
第三种形式:将依赖注入到有参构造方法中
//Service层类实例化Bean的注解
@Service
public class UserService {
    private UserDao userDao;

    //不用注解,也可以注入
//    public UserService(UserDao userDao){
//        this.userDao = userDao;
//    }

    //根据类型依赖注入
    @Autowired
                    //根据名称依赖注入
    public UserService(@Qualifier("userDao") UserDao userDao){
        this.userDao = userDao;
    }


    public User userDao(Integer id) {
        return userDao.findOne(id);
    }
}
测试
@Test
public void testAnnotation() {
    ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
    UserService userService = app.getBean("userService", UserService.class);
    User one = userService.userDao(01);
    System.out.println(one);
}

注入基本类型属性

第一种形式:将依赖注入到私有属性
//Service层类实例化Bean的注解
@Service
public class UserService {
    ////根据类型依赖注入
    @Autowired
    private UserDao userDao;

    //注入普通属性
    @Value("UserService")
    private String serviceName;

    public User userDao(Integer id) {
        System.out.println(serviceName);
        return userDao.findOne(id);
    }
}
第二种方式:将依赖注入到setter方法中
//Service层类实例化Bean的注解
@Service
public class UserService {
    ////根据类型依赖注入
    @Autowired
    private UserDao userDao;

    private String serviceName;

    @Value("UserService")
    public void setUserDao(String serviceName){
        this.serviceName = serviceName;
    }

    public User userDao(Integer id) {
        System.out.println(serviceName);
        return userDao.findOne(id);
    }
}
第三种形式:将依赖注入到有参构造方法中
//Service层类实例化Bean的注解
@Service
public class UserService {
    ////根据类型依赖注入
    @Autowired
    private UserDao userDao;

    private String serviceName;

    public UserService(@Value("UserService") String serviceName){
        this.serviceName = serviceName;
    }

    public User userDao(Integer id) {
        System.out.println(serviceName);
        return userDao.findOne(id);
    }
}
测试
@Test
public void testAnnotation() {
    ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
    UserService userService = app.getBean("userService", UserService.class);
    User one = userService.userDao(01);
    System.out.println(one);
}

注入外部属性文件

    <!--    开启组件扫描-->
    <context:component-scan base-package="com.ccl"/>
    <!--    引入外部文件-->
    <context:property-placeholder location="classpath:admin.properties"/>
//Service层类实例化Bean的注解
@Service
public class UserService {
    ////根据类型依赖注入
    @Autowired
    private UserDao userDao;

    public User userDao(Integer id) {
        return userDao.findOne(id);
    }
}
第一种形式:将依赖注入到私有属性
//Service层类实例化Bean的注解
@Service
public class UserService {
    ////根据类型依赖注入
    @Autowired
    private UserDao userDao;

    public User userDao(Integer id) {
        return userDao.findOne(id);
    }
}
第二种方式:将依赖注入到setter方法中
//Dao层类实例化Bean的注解
@Repository()
public class UserDao {
    private String username;
    private String password;

    //注入普通属性
    @Value("${admin.username}")
    private void setUsername(String username) {
        this.username = username;
    }

    //注入普通属性
    @Value("${admin.password}")
    private void setPassword(String password) {

        this.password = password;
    }

    public User findOne(Integer id) {
        System.out.println("admin username:" + username);
        System.out.println("admin password:" + password);
        return new User(01, "leesin", "man");
    }
}
第三种形式:将依赖注入到有参构造方法中
//Dao层类实例化Bean的注解
@Repository()
public class UserDao {
    private String username;
    private String password;

    //注入普通属性
    public UserDao(@Value("${admin.username}") String username, @Value("${admin.password}") String password) {
        this.username = username;
        this.password = password;
    }
    
    public User findOne(Integer id) {
        System.out.println("admin username:" + username);
        System.out.println("admin password:" + password);
        return new User(01, "leesin", "man");
    }
}
测试
@Test
public void testAnnotation() {
    ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
    UserService userService = app.getBean("userService", UserService.class);
    User one = userService.userDao(01);
    System.out.println(one);
}

Bean的作用域

单例模式
@Component
@Scope("singleton")
public class Person {
}
//下图为测试代码输出,单例模式下无论实例化多少个bean对象,都是指向同一个内存地址

image-20210521225242375

多例模式
@Component
@Scope("prototype")
public class Person {
}
//下图为测试代码输出,多例模式下实例化多个bean对象,都是指向不同的内存地址

image-20210521225436870

测试
@Test
public void testScope(){
    ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
    Person person0 = app.getBean("person", Person.class);
    System.out.println(person0);
    Person person1 = app.getBean("person", Person.class);
    System.out.println(person1);
}

全注解开发

image-20210522114555592

注解说明

注解 说明
@Configuration 用于指定当前类是一个Spring配置类,当创建容器时会从该类上加载注解
@ComPonentScan 用于指定Spring在初始化容器时要扫描的包。

作用在Spring的xml配置文件中的<context:component-scan base-package="com.ccl"/>一样
@Bean 使用在方法上,标注将该方法的放回值存储到Spring容器中
@PropertySource 用于加载properties文件中的配置
@Import 用于导入其他配置类
@Prifile 用于指定类或方法在特定的Profile环境生效

导入依赖

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.2</version>
    </dependency>
    
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13.1</version>
        <scope>test</scope>
    </dependency>
    
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.20</version>
    </dependency>
</dependencies>

扫描式注册

声明一个组件

@Component
public class User {
}

扫描配置

//指定当前类为配置类,此类替代了applicationContext.xml
@Configuration
//指定要扫描的包,作用和xml中<context:component-scan base-package="com.ccl.bean"/>一样
//@ComponentScan("com.ccl")

//按组件配置扫描
@ComponentScan(basePackages = "com.ccl",
        //只扫描《注解类型》,注解类型为:@Component--
        includeFilters = {
                @ComponentScan.Filter(classes = Component.class)
        },
        //不扫描《注解类型》,注解类型为:@Repository、@Service、@Controller
        excludeFilters = {
                @ComponentScan.Filter(classes = Repository.class),
                @ComponentScan.Filter(classes = Service.class),
                @ComponentScan.Filter(classes = Controller.class)
        }
)
public class AppConfig {
}

创建IOC容器的方式

@Test
public void test() {
    ApplicationContext app = new AnnotationConfigApplicationContext(AppConfig.class);
    User user = app.getBean("user", User.class);
    System.out.println(user);
}

配置式注册

//指定当前类为配置类,此类替代了applicationContext.xml
@Configuration
public class AppConfig {
    @Bean
    public User user() {
        return new User();
    }
}

此@Bean等同于applicationContext中的

<beans>
	<bean id="user" class="com.ccl.bean.User"/>
</beans>

需要注意的是 proxyBeanMethods 属性:

@Configuration(proxyBeanMethods = true) :代表Full模式,外部无论对配置类中的这个组件注册方法调用多少次获取的都是之前注册容器中的单实例对象
@Configuration(proxyBeanMethods = false) :代表Lite模式,外部无论对配置类中的这个组件注册方法调用多少次获取的都是创建一个新的实例对象

环境切换

@Setter
@ToString
public class DataSource {
    private String driverClass;
    private String url;
    private String username;
    private String password;
}
//指定当前类为配置类,此类替代了applicationContext.xml
@Configuration()
//指定要扫描的包,作用和xml中<context:component-scan base-package="com.ccl.bean"/>一样
@ComponentScan("com.ccl")
public class AppConfig {

    @Bean("dataSource")
    //用于指定类或方法在特定的Profile环境生效
    @Profile({"development","default"})
    public DataSource dataSourceDevelopment(){
        DataSource dataSource = new DataSource();
        dataSource.setDriverClass("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3308/");
        dataSource.setUsername("root");
        dataSource.setPassword("asd");
        return dataSource;
    }

    @Bean("dataSource")
    //用于指定类或方法在特定的Profile环境生效
    @Profile("production")
    public DataSource dataSourceProduction(){
        DataSource dataSource = new DataSource();
        dataSource.setDriverClass("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://ccl.com:3308/");
        dataSource.setUsername("root");
        dataSource.setPassword("asd");
        return dataSource;
    }
}
@Test
public void test1(){
    AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext();
    //激活环境
    app.getEnvironment().setActiveProfiles("production");
    //注册配置对象
    app.register(AppConfig.class);
    //启动刷新容器
    app.refresh();

    DataSource dataSource = app.getBean("dataSource", DataSource.class);
    System.out.println(dataSource);
}

属性配置导入

#开发环境数据库配置
jdbc.dev.driverClass=com.mysql.jdbc.Driver
jdbc.dev.url=jdbc:mysql://localhost:3308/
jdbc.dev.username=root
jdbc.dev.password=123456

#生产环境数据库配置
jdbc.pro.driverClass=com.mysql.jdbc.Driver
jdbc.pro.url=jdbc:mysql://ccl.com:3308/
jdbc.pro.username=root
jdbc.pro.password=456789

//指定当前类为配置类,此类替代了applicationContext.xml
@Configuration()
//指定要扫描的包,作用和xml中<context:component-scan base-package="com.ccl.bean"/>一样
@ComponentScan("com.ccl")
//加载.properties文件配置
@PropertySource("classpath:jdbc.properties")
public class AppConfig {

    //通过env获取
    @Autowired
    Environment env;

    @Bean("dataSource")
    @Profile({"davelopment","default"})
    public DataSource dataSourceDevelopment(){
        DataSource dataSource = new DataSource();
        dataSource.setDriverClass(env.getProperty("jdbc.dev.driverClass"));
        dataSource.setUrl(env.getProperty("jdbc.dev.url"));
        dataSource.setUsername(env.getProperty("jdbc.dev.username"));
        dataSource.setPassword(env.getProperty("jdbc.dev.password"));
        return dataSource;
    }

    //通过@Value注入
    @Value("${jdbc.pro.driverClass}")
    private String driverClass;
    @Value("${jdbc.pro.url}")
    private String url;
    @Value("${jdbc.pro.username}")
    private String username;
    @Value("${jdbc.pro.password}")
    private String password;

    @Bean("dataSource")
    @Profile("production")
    public DataSource dataSourceProduction(){
        DataSource dataSource = new DataSource();
        dataSource.setDriverClass(driverClass);
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        return dataSource;
    }
}
@Test
public void test2(){
    AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext();
    //配置激活环境
    app.getEnvironment().setActiveProfiles("production");
    //注册配置对象
    app.register(AppConfig.class);
    //启动刷新容器
    app.refresh();

    DataSource dataSource = app.getBean("dataSource",DataSource.class);
    System.out.println(dataSource);
}

普通对象导入

@Setter
@ToString
public class Person {
    private String name;
    private String gender;
}
//指定当前类为配置类,此类替代了applicationContext.xml
@Configuration()
//指定要扫描的包,作用和xml中<context:component-scan base-package="com.ccl.bean"/>一样
@ComponentScan("com.ccl")
//加载.properties文件配置
@PropertySource("classpath:jdbc.properties")
//给容器中自动创建出这个类的组件,默认组件的名字就是全类名
@Import(Person.class)
public class AppConfig {
	...
}
@Test
public void test3() {
    AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(AppConfig.class);
    Person person = app.getBean("com.ccl.bean.Person", Person.class);
    System.out.println(person);
}

原生配置导入

@Setter
@ToString
public class Dog {
    private String name;
    private String color;
}

src/main/resources/applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean class="com.ccl.bean.Dog"/>
</beans>
//指定当前类为配置类,此类替代了applicationContext.xml
@Configuration()
//指定要扫描的包,作用和xml中<context:component-scan base-package="com.ccl.bean"/>一样
@ComponentScan("com.ccl")
//加载.properties文件配置
@PropertySource("classpath:jdbc.properties")
//给容器中自动创建出这个类的组件,默认组件的名字就是全类名
@Import(Person.class)
//引入Spring配置文件
@ImportResource("classpath:applicationContext.xml")
public class AppConfig {
	...
}
@Test
public void test4(){
AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(AppConfig.class);
Dog dog = app.getBean("com.ccl.bean.Dog", Dog.class);
System.out.println(dog);
}

AOP入门

环境准备

pom

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.2</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>5.3.2</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.20</version>
    </dependency>
</dependencies>

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"

        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
">

</beans>

术语介绍

连接点

​ 英文名:Join Point,代表类中有哪些方法可以被增强,这些可以被增强的方法称为连接点。

切入点

​ 英文名:Pointcut,类中虽然有很多方法可以被增强,但是只有实际被增强的方法称为切入点。

​ 表达式:execution([权限修饰符] 返回类型 包名.类名.方法名(参数))

通知

​ 英文名:Advice,通知描述了新增加的代码段在切入点处何时执行。

其分类:

​ 前置通知,Before Advice
​ 后置通知,AfterReturning Advice
​ 环绕通知,Around Advice
​ 异常通知,AfterThrowing Advice
​ 最终通知,After Advice

目标对象

​ 英文:Target Object,被增强的对象称为目标对象,由于AOP框架是使用动态代理实现的,因此该对象始终是代理对象。

代理对象

​ 英文:Proxy Object,由AOP框架在程序运行期间动态创建的一个对象。

织入

​ 英文名:Weaving,把通知应用到目标对象的切入点的过程就是织入。

切面

​ 英文名:Aspect,切面是一系列通知和切入点的结合。

AOP配置(XML)

image-20210522220045874

pom

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.2</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>5.3.2</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.20</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13.1</version>
        <scope>test</scope>
    </dependency>
</dependencies>
public class UserDaoImpl {
    public void save() {
        System.out.println("UserDaoImpl save ...");
    }

    public Integer delete() {
        System.out.println("UserDaoImpl delete ...");
        return 10086;
    }

    public void update() {
        System.out.println("UserDaoImpl update ...");
        int i = 1 / 0;
    }

    public void find() {
        for (int i = 0; i < 100000; i++) { }
        System.out.println("UserDaoImpl find ...");
    }
}
public class SysCheck {
    public void check(){
        System.out.println("SysCheck 权限校验 ...");
    }
}
public class SysLog {
    public void log(Object result) {
        System.out.println("SysLog 日志记录,删除主键:" + result);
    }
}
public class SysMonitor {
    public void monitor(ProceedingJoinPoint joinPoint) throws Throwable {
        //开始执行时间
        long startTime = System.currentTimeMillis();
        //正式执行方法
        joinPoint.proceed();
        //结束执行时间
        long endTime = System.currentTimeMillis();
        //输出时间差值
        System.out.println("SysMonitor 耗费时间:" + (endTime - startTime) + "ms");
    }
}
public class SysEmail {
    public void email(Throwable throwable) {
        System.out.println("SysEmail 发送邮件,异常信息:" + throwable.getMessage());
    }
}
public class SysCounter {
    private static Integer count = 0;

    public void counter() {
        System.out.println("SysCounter 调用次数:" + (++count));
    }
}

applicationContext.xml

<bean id="userDaoImpl" class="com.ccl.dao.UserDaoImpl"/>
<bean id="sysCheck" class="com.ccl.enhance.SysCheck"/>
<bean id="sysLog" class="com.ccl.enhance.SysLog"/>
<bean id="sysMonitor" class="com.ccl.enhance.SysMonitor"/>
<bean id="sysEmail" class="com.ccl.enhance.SysEmail"/>
<bean id="sysCounter" class="com.ccl.enhance.SysCounter"/>


<!--    aop配置-->
<aop:config>
    <!--        配置切入点:删除方法-->
    <aop:pointcut id="deleteMethodEnhance" expression="execution(* com.ccl.dao.UserDaoImpl.delete(..))"/>
    <!--        配置切入点:查找方法-->
    <aop:pointcut id="findMethodEnhance" expression="execution(* com.ccl.dao.UserDaoImpl.find(..))"/>
    <!--        配置切入点:更新方法-->
    <aop:pointcut id="updateMethodEnhance" expression="execution(* com.ccl.dao.UserDaoImpl.update(..))"/>
    <!--        配置切入点:所有方法-->
    <aop:pointcut id="allMethodEnhance" expression="execution(* com.ccl.dao.UserDaoImpl.*(..))"/>

    <!--        配置切面:调用计数-->
    <aop:aspect ref="sysCounter">
        <aop:after method="counter" pointcut-ref="allMethodEnhance"/>
    </aop:aspect>
    <!--        配置切面:发送邮件-->
    <aop:aspect ref="sysEmail">
        <aop:after-throwing method="email" pointcut-ref="updateMethodEnhance" throwing="throwable"/>
    </aop:aspect>
    <!--        配置切面:性能监控-->
    <aop:aspect ref="sysMonitor">
        <aop:around method="monitor" pointcut-ref="findMethodEnhance"/>
    </aop:aspect>
    <!--        配置切面:日志记录-->
    <aop:aspect ref="sysLog">
        <aop:after-returning method="log" pointcut-ref="deleteMethodEnhance" returning="result"/>
    </aop:aspect>
    <!--        配置切面:权限校验-->
    <aop:aspect ref="sysCheck">
        <aop:before method="check" pointcut-ref="deleteMethodEnhance"/>
    </aop:aspect>

AOP配置(注解方式)

半注解开发

注解说明

注解 说明
@Aspect 定义切面类,将被标识的类作为一个切面bean
@Pointcut 切点
@Around 环绕增强
@Before 前置增强
@AfterReturning 后置增强-方法正常退出时执行
@AfterThrowing 后置增强-方法异常执行
@After 后置增强-增强anyway

image-20210522225520385

导入依赖

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.2</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>5.3.2</version>
    </dependency>
</dependencies>

命名空间

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
">
    <!--开启组件扫描-->
    <context:component-scan base-package="com.caochenlei"/>

    <!--开启AOP代理-->
    <aop:aspectj-autoproxy/>
</beans>
//Dao层类实例化Bean的注解
@Repository
public class UserDaoImpl {
    public void save() {
        System.out.println("UserDaoImpl save ...");
    }

    public Integer delete() {
        System.out.println("UserDaoImpl delete ...");
        return 10086;
    }

    public void update() {
        System.out.println("UserDaoImpl update ...");
        int i = 1 / 0;
    }

    public void find() {
        for (int i = 0; i < 100000; i++) {
        }
        System.out.println("UserDaoImpl find ...");
    }
}
//普通类实例化Bean的注解
@Component
//定义切面类,将被标识的类作为一个切面bean
@Aspect
public class SysCheck {
    //切点
    @Pointcut("execution(* com.ccl.dao.UserDaoImpl.delete(..))")
    public void deleteMethodEnhance() {
    }

    //前置增强
    @Before("deleteMethodEnhance()")
    public void check(){
        System.out.println("SysCheck 权限验证...");
    }
}
//普通类实例化Bean的注解
@Component
//定义切面类,将被标识的类作为一个切面bean
@Aspect
public class SysCounter {
    private static Integer count = 0;

    //切点
    @Pointcut("execution(* com.ccl.dao.UserDaoImpl.*(..))")
    public void allMethodEnhance() {}

    //后置增强
    @After("com.ccl.enhance.SysCheck.deleteMethodEnhance()")
    public void counter() {
        System.out.println("SysCounter 调用次数:" + (++count));
    }
}
//普通类实例化Bean的注解
@Component
//定义切面类,将被标识的类作为一个切面bean
@Aspect
public class SysEmail {
    //切点
    @Pointcut("execution(* com.ccl.dao.UserDaoImpl.update(..))")
    public void updateMethodEnhance() {}

    //后置增强-方法异常执行
    @AfterThrowing(value = "updateMethodEnhance()", throwing = "throwable")
    public void email(Throwable throwable) {
        System.out.println("SysEmail 发送邮件,异常信息:" + throwable.getMessage());
    }
}
//普通类实例化Bean的注解
@Component
//定义切面类,将被标识的类作为一个切面bean
@Aspect
public class SysLog {
    //切点
    @Pointcut("execution(* com.ccl.dao.UserDaoImpl.delete(..))")
    public void deleteMethodEnhance() {}

    //后置增强-方法正常退出时执行
    @AfterReturning(value = "deleteMethodEnhance()",returning = "result")
    public void log(Object result) {
        System.out.println("SysLog 日志记录,删除主键:" + result);
    }
}
//普通类实例化Bean的注解
@Component
//定义切面类,将被标识的类作为一个切面bean
@Aspect
public class SysMonitor {
    //切点
    @Pointcut("execution(* com.ccl.dao.UserDaoImpl.find(..))")
    public void findMethodEnhance() {}

    //环绕增强
    @Around("findMethodEnhance()")
    public void monitor(ProceedingJoinPoint joinPoint) throws Throwable {
        //开始执行时间
        long startTime = System.currentTimeMillis();
        //正式执行方法
        joinPoint.proceed();
        //结束执行时间
        long endTime = System.currentTimeMillis();
        //输出时间差值
        System.out.println("SysMonitor 耗费时间:" + (endTime - startTime) + "ms");
    }
}

测试

@Test
public void test(){
    ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
    UserDaoImpl userDaoImpl = app.getBean("userDaoImpl", UserDaoImpl.class);
    userDaoImpl.save();
    userDaoImpl.update();
    userDaoImpl.find();
    userDaoImpl.delete();
}

全注解开发

导入依赖

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.2</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>5.3.2</version>
    </dependency>
</dependencies>
//Dao层类实例化Bean的注解
@Repository
public class UserDaoImpl {
    public void save() {
        System.out.println("UserDaoImpl save ...");
    }

    public Integer delete() {
        System.out.println("UserDaoImpl delete ...");
        return 10086;
    }

    public void update() {
        System.out.println("UserDaoImpl update ...");
        int i = 1 / 0;
    }

    public void find() {
        for (int i = 0; i < 100000; i++) {
        }
        System.out.println("UserDaoImpl find ...");
    }
}
//普通类实例化Bean的注解
@Component
//定义切面类,将被标识的类作为一个切面bean
@Aspect
public class SysCheck {
    //切点
    @Pointcut("execution(* com.ccl.dao.UserDaoImpl.delete(..))")
    public void deleteMethodEnhance() {
    }

    //前置增强
    @Before("deleteMethodEnhance()")
    public void check(){
        System.out.println("SysCheck 权限验证...");
    }
}
//普通类实例化Bean的注解
@Component
//定义切面类,将被标识的类作为一个切面bean
@Aspect
public class SysCounter {
    private static Integer count = 0;

    //切点
    @Pointcut("execution(* com.ccl.dao.UserDaoImpl.*(..))")
    public void allMethodEnhance() {}

    //后置增强
    @After("com.ccl.enhance.SysCheck.deleteMethodEnhance()")
    public void counter() {
        System.out.println("SysCounter 调用次数:" + (++count));
    }
}
//普通类实例化Bean的注解
@Component
//定义切面类,将被标识的类作为一个切面bean
@Aspect
public class SysEmail {
    //切点
    @Pointcut("execution(* com.ccl.dao.UserDaoImpl.update(..))")
    public void updateMethodEnhance() {}

    //后置增强-方法异常执行
    @AfterThrowing(value = "updateMethodEnhance()", throwing = "throwable")
    public void email(Throwable throwable) {
        System.out.println("SysEmail 发送邮件,异常信息:" + throwable.getMessage());
    }
}
//普通类实例化Bean的注解
@Component
//定义切面类,将被标识的类作为一个切面bean
@Aspect
public class SysLog {
    //切点
    @Pointcut("execution(* com.ccl.dao.UserDaoImpl.delete(..))")
    public void deleteMethodEnhance() {}

    //后置增强-方法正常退出时执行
    @AfterReturning(value = "deleteMethodEnhance()",returning = "result")
    public void log(Object result) {
        System.out.println("SysLog 日志记录,删除主键:" + result);
    }
}
//普通类实例化Bean的注解
@Component
//定义切面类,将被标识的类作为一个切面bean
@Aspect
public class SysMonitor {
    //切点
    @Pointcut("execution(* com.ccl.dao.UserDaoImpl.find(..))")
    public void findMethodEnhance() {}

    //环绕增强
    @Around("findMethodEnhance()")
    public void monitor(ProceedingJoinPoint joinPoint) throws Throwable {
        //开始执行时间
        long startTime = System.currentTimeMillis();
        //正式执行方法
        joinPoint.proceed();
        //结束执行时间
        long endTime = System.currentTimeMillis();
        //输出时间差值
        System.out.println("SysMonitor 耗费时间:" + (endTime - startTime) + "ms");
    }
}
//指定当前类为配置类,此类替代了applicationContext.xml
@Configuration
//指定要扫描的包,作用和xml中<context:component-scan base-package="com.ccl.bean"/>一样
@ComponentScan("com.ccl")
//相当于在传统的xml配置文件中添加 <aop:aspectj-autoproxy>一样。
@EnableAspectJAutoProxy
public class AppConfig {
}

测试

@Test
public void test(){
    AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(AppConfig.class);
    UserDaoImpl userDaoImpl = app.getBean("userDaoImpl", UserDaoImpl.class);
    userDaoImpl.save();
    userDaoImpl.update();
    userDaoImpl.find();
    userDaoImpl.delete();
}

多个切面增强同一切点指定优先级:

在切面@Aspect上使用注解@Order(整数)来标识当前切面下所有的通知的优先级,数字越小,优先级越高,优先级越高就越先执行。

posted @ 2021-05-20 23:43  yonugleesin  阅读(52)  评论(0编辑  收藏  举报