最详细的spring(IOC、AOP)教程

Spring(IOC、AOP)

概述

spring是一个轻量级的开源的javaEE框架

spring可以解决企业开发的复杂性

特点

  1. 方便解耦,简化开发
  2. AOP编程支持
  3. 方便程序测试
  4. 方便集成各种优秀框架
  5. 降低javaEE API使用难度
  6. 方便进行事务的操作

入门案例

创建maven工程,导入依赖

 <!-- spring的依赖,导入这一个就可以了,它会把它所依赖的spring-beans、spring-core、spring-expression都导入进来 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.6.RELEASE</version>
        </dependency>
        <!-- spring依赖的日志包 -->
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.1.1</version>
        </dependency>
		<!-- 测试包 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

编写User类

public class User {

  public void test(){
    System.out.println("User。。。。。。。。。。。。。");
  }
}

编写bean.xml,这是spring的配置文件

<?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 id="user" class="com.sixa.spring5.User"></bean>

</beans>

编写测试类

@Test
  public void test1(){
    //加载配置文件
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
    //获取配置的bean
    User user = (User) applicationContext.getBean("user");
    user.test();     //User。。。。。。。。。。。。。
  }

IOC

控制反转(Inversion of Control),是面向对象编程的一种设计原则,可以用来减低计算机代码之间的耦合度。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。

把对象的创建和对象之间的调用过程都交给spring去管理

底层原理

  1. xml解析,工程模式,反射

在这里插入图片描述

  1. IOC 接口

    IOC思想基于IOC容器完成,IOC容器底层就是对象工厂

    spring提供IOC容器实现两种方式:

    ​ 1)BeanFactory:

    ​ IOC容器基本实现,是spring内部使用的接口,一般不推荐开发人员使用

    ​ 它在加载配置文件的时候不会去创建对象,在获取(使用)对象的时候才会去创建对象

    ​ 2)ApplicationContext:

    ​ BeanFactory的子接口,提供更多更强大的功能,一般推荐开发人员使用

    ​ 在加载配置文件的时候就会创建对象

  2. ApplicationContext接口的实现类

    FileSystemXmlApplicationContext:对应盘符路径

    ClassPathXmlApplicationContext:对应类路径

Bean管理

spring创建对象

spring注入属性

####1.基于xml方式

创建对象

<bean id="user" class="com.sixa.spring5.User"></bean> <!-- 默认会去调用无参构造 -->

#####注入属性(DI)

######1)set方法注入

User类

public class User {

  private String name;

  public void setName(String name){
    this.name = name;
  }
  
  public void test(){
    System.out.println("name:"+name);
  }
}

bean.xml配置

 <bean id="user" class="com.sixa.spring5.User">
        <property name="name" value="张三"></property>
 </bean>

测试

@Test
  public void test1(){
    //加载配置文件
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
    //获取配置的bean
    User user = (User) applicationContext.getBean("user");
    user.test();  //name:张三
  }

######2)有参构造方法注入

User类

public class User {

  private String name;
  private int age;

  public User(String name, int age) {
    this.name = name;
    this.age = age;
  }

  public void test(){
    System.out.println("name:"+name+",age:"+age);
  }
}

bean.xml配置

 <bean id="user" class="com.sixa.spring5.User">
        <constructor-arg name="name" value="张三"></constructor-arg>
        <constructor-arg name="age" value="20"></constructor-arg>
        <!-- 用索引方法注入
        <constructor-arg index="0" value="张三"></constructor-arg>
        <constructor-arg index="1" value="20"></constructor-arg>
        -->
 </bean>

测试

 @Test
  public void test1(){
    //加载配置文件
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
    //获取配置的bean
    User user = (User) applicationContext.getBean("user");
    user.test(); //name:张三,age:20
  }

#####其他属性注入

######字面量

1.注入null值

User类

public class User {

  private String name;
  private int age;

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

  public void setAge(int age) {
    this.age = age;
  }

  public void test(){
    System.out.println("name:"+name+",age:"+age);
  }
}

bean.xml配置

  <bean id="user" class="com.sixa.spring5.User">
        <!-- 注入null -->
        <property name="name">
            <null></null>
        </property>
       
        <property name="age" value="20"></property>
  </bean>

测试

 @Test
  public void test1(){
    //加载配置文件
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
    //获取配置的bean
    User user = (User) applicationContext.getBean("user");
    user.test();  //name:null,age:20
  }

2.注入特殊符号

bean.xml配置

  <bean id="user" class="com.sixa.spring5.User">
        <property name="name">
            <value><![CDATA[<<张三>>]]></value>
        </property>

        <property name="age" value="20"></property>
  </bean>

测试

 @Test
  public void test1(){
    //加载配置文件
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
    //获取配置的bean
    User user = (User) applicationContext.getBean("user");
    user.test();  //name:<<张三>>,age:20
  }

######外部bean注入

UserDao接口

public interface UserDao {
  void add();
}

UserDaoImp类

public class UserDaoImp implements UserDao {
  @Override
  public void add() {
    System.out.println("add.................");
  }
}

UserService接口

public interface UserService {
  void add();
}

UserServiceImp类

public class UserServiceImp implements UserService {
  private UserDao userDao;
  public void setUserDao(UserDao userDao){
    this.userDao = userDao;
  }
  @Override
  public void add() {
    userDao.add();
  }
}

bean.xml配置

<bean id="userService" class="com.sixa.spring5.service.UserServiceImp">
        <property name="userDao" ref="userDao"></property>
</bean>

<bean id="userDao" class="com.sixa.spring5.dao.UserDaoImp"></bean>

测试

 @Test
  public void test2(){
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
    UserService userService = (UserService) applicationContext.getBean("userService");
    userService.add();  //add.................
  }

######内部bean注入

Dept类

public class Dept {
  private String name;

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

  @Override
  public String toString() {
    return "Dept{" +
            "name='" + name + '\'' +
            '}';
  }
}

User类

public class User {

  private String name;
  private int age;
  private Dept dept;

  public void setDept(Dept dept) {
    this.dept = dept;
  }

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

  public void setAge(int age) {
    this.age = age;
  }

  public void test(){
    System.out.println("name:"+name+",age:"+age+",dept:"+dept);
  }
}

bean.xml配置

 <bean id="user" class="com.sixa.spring5.User">
        <property name="name" value="张三"></property>
        <property name="age" value="20"></property>
        <property name="dept">
            <bean id="dept" class="com.sixa.spring5.Dept">
                <property name="name" value="开发部"></property>
            </bean>
        </property>
  </bean>

测试

@Test
public void test1(){
  //加载配置文件
  ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
  //获取配置的bean
  User user = (User) applicationContext.getBean("user");
  user.test();  //name:张三,age:20,dept:Dept{name='开发部'}
}

######级联赋值

第一种写法

 <bean id="user" class="com.sixa.spring5.User">
        <property name="name" value="张三"></property>
        <property name="age" value="20"></property>
        <property name="dept" ref="dept"></property>
 </bean>
 <bean id="dept" class="com.sixa.spring5.Dept">
        <property name="name" value="开发部"></property>
 </bean>

第二种写法,需要在User类中有getDept方法

User类

public class User {

  private String name;
  private int age;
  private Dept dept;

  public void setDept(Dept dept) {
    this.dept = dept;
  }

  //需要有get方法
  public Dept getDept() {
    return dept;
  }

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

  public void setAge(int age) {
    this.age = age;
  }

  public void test(){
    System.out.println("name:"+name+",age:"+age+",dept:"+dept);
  }
}

bean.xml配置

<bean id="user" class="com.sixa.spring5.User">
        <property name="name" value="张三"></property>
        <property name="age" value="20"></property>
        <property name="dept" ref="dept"></property>
        <property name="dept.name" value="软件部"></property>
</bean>
<bean id="dept" class="com.sixa.spring5.Dept">
     <property name="name" value="开发部"></property>
</bean>

测试

  @Test
  public void test1(){
    //加载配置文件
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
    //获取配置的bean
    User user = (User) applicationContext.getBean("user");
    user.test();  //name:张三,age:20,dept:Dept{name='软件部'}
  }
注入集合类型属性

Course类

public class Course {
  private String name;

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

  @Override
  public String toString() {
    return "Course{" +
            "name='" + name + '\'' +
            '}';
  }
}

Student类

public class User {

  private String name;
  private int age;
  private Dept dept;

  public void setDept(Dept dept) {
    this.dept = dept;
  }

  public Dept getDept() {
    return dept;
  }

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

  public void setAge(int age) {
    this.age = age;
  }

  public void test(){
    System.out.println("name:"+name+",age:"+age+",dept:"+dept);
  }
}

bean.xml配置

     <bean id="student" class="com.sixa.spring5.Student">
        <!-- 注入数组属性 -->
        <property name="arrays">
            <array>
                <value>java基础</value>
                <value>数据库</value>
            </array>
        </property>
        <!-- 注入list属性 -->
        <property name="list">
            <list>
                <value>张三</value>
                <value>小三</value>
            </list>
        </property>
        <!-- 注入map属性 -->
        <property name="maps">
            <map>
                <entry key="JAVA" value="java"></entry>
                <entry key="PHP" value="php"></entry>
            </map>
        </property>
        <!-- 注入set属性 -->
        <property name="sets">
            <set>
                <value>redis</value>
                <value>MySQL</value>
            </set>
        </property>
        <!-- 注入list属性,集合类型是对象 -->
        <property name="courseList">
            <list>
                <ref bean="course1"></ref>
                <ref bean="course2"></ref>
            </list>
        </property>
    </bean>
    <bean id="course1" class="com.sixa.spring5.Course">
        <property name="name" value="spring"></property>
    </bean>
    <bean id="course2" class="com.sixa.spring5.Course">
        <property name="name" value="mybatis"></property>
    </bean>

测试

  @Test
  public void test3(){
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
    Student student = (Student)applicationContext.getBean("student");
    System.out.println(student.toString()); //Student{arrays=[java基础, 数据库], list=[张三, 小三], maps={JAVA=java, PHP=php}, sets=[redis, MySQL], courseList=[Course{name='spring'}, Course{name='mybatis'}
  }
提取公共集合

Book类

public class Book {
  private List<String> books;

  public void setBooks(List<String> books) {
    this.books = books;
  }

  @Override
  public String toString() {
    return "Book{" +
            "books=" + books +
            '}';
  }
}

bean.xml配置

更改约束文件

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:util="http://www.springframework.org/schema/util"
       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/util http://www.springframework.org/schema/util/spring-util.xsd">

bean配置

    <util:list id="bookList">
        <value>三体1</value>
        <value>三体2</value>
        <value>三体3</value>
    </util:list>

    <bean id="book" class="com.sixa.spring5.Book">
        <property name="books" ref="bookList"></property>
    </bean>

测试

  @Test
  public void test4(){
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
    Book book = (Book)applicationContext.getBean("book");
    System.out.println(book); //Book{books=[三体1, 三体2, 三体3]}
  }
2.基于注解

bean2.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 https://www.springframework.org/schema/context/spring-context.xsd">
  
    <context:component-scan base-package="com.sixa.spring5"></context:component-scan>

</beans>

UserDaoImp类

package com.sixa.spring5.dao;

import org.springframework.stereotype.Repository;
//注解分类Component,Controller(控制层),Service(service层),Repository(dao层)
//其实这些注解的作用都一样,你也可以把Service注解加到控制层,但是大家都会遵守这个规范
//value可以省略,默认是该类的类名首字母小写
@Repository(value = "userDao")
public class UserDaoImp implements UserDao {
  @Override
  public void add() {
    System.out.println("add.................");
  }
}

测试

  @Test
  public void test8(){
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean2.xml");
    UserDao userDao = (UserDao) applicationContext.getBean("userDao");
    userDao.add();  //add.................
  }

扫描细节配置

    <!-- 不使用默认的过滤器,只扫描Repository注解的类 -->
    <context:component-scan base-package="com.sixa.spring5" use-default-filters="false">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
    </context:component-scan>
    
	<!-- 使用默认的过滤器,但不扫描Component注解的类 -->
    <context:component-scan base-package="com.sixa.spring5">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Component"/>
    </context:component-scan>
基于注解注入属性

@Autowired(根据属性类型进行自动注入)、@Qualifier(根据属性名称注入)、@Resource(可以根据属性类型或属性名称注入)

@Value(注入普通类型)

开启注解扫描

<?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 https://www.springframework.org/schema/context/spring-context.xsd">
    <context:component-scan base-package="com.sixa.spring5"></context:component-scan>


</beans>

UserDaoImp类

@Repository
public class UserDaoImp implements UserDao {
  @Override
  public void add() {
    System.out.println("dao add.................");
  }
}

UserServiceImp类

@Service
public class UserServiceImp implements UserService {
  //不需要写set方法
  @Autowired
  private UserDao userDao;
  @Override
  public void add() {
    System.out.println("service add............");
    userDao.add();
  }
}

测试

  @Test
  public void test9(){
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean2.xml");
    UserService userService = (UserService) applicationContext.getBean("userServiceImp");
    userService.add(); //service add............
                       //dao add.................
  }

@Qualifier要配合@Autowired一起使用

@Service
public class UserServiceImp implements UserService {
  //不需要写set方法
  @Qualifier("userDaoImp")
  @Autowired
  private UserDao userDao;
  @Override
  public void add() {
    System.out.println("service add............");
    userDao.add();
  }
}

@Resource 既可以根据类型注入,也可以根据名称注入

@Service
public class UserServiceImp implements UserService {
  //@Resource  根据类型注入
  @Resource(name = "userDaoImp") //根据名称注入
  private UserDao userDao;
  @Override
  public void add() {
    System.out.println("service add............");
    userDao.add();
  }
}

@Value 注入普通属性

  @Value(value = "abc")
  private String name;

#####完全注解开发

编写配置类

@Configuration
@ComponentScan(basePackages = "com.sixa.spring5")
public class SpringConfig {

}

测试

  @Test
  public void test9(){                           //注意new的是这个类的对象
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
    UserService userService = (UserService) applicationContext.getBean("userServiceImp");
    userService.add();
  }

FactoryBean

普通bean:在配置文件中定义bean的类型就是返回的类型

工厂bean:在配置文件中定义bean的类型可以和返回类型不一样

People类

public class People {
  private String name;

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

  @Override
  public String toString() {
    return "People{" +
            "name='" + name + '\'' +
            '}';
  }
}

MyBean类,实现FactoryBean接口

public class MyBean implements FactoryBean<People> {
  @Override
  public People getObject() throws Exception {
    People people = new People();//返回的类型取决于这的类型
    people.setName("张三");
    return people;
  }

  @Override
  public Class<?> getObjectType() {
    return null;
  }

  @Override
  public boolean isSingleton() {
    return false;
  }
}

bean.xml配置

 <bean id="myBean" class="com.sixa.spring5.MyBean"></bean>

测试

  @Test
  public void test5(){
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
    People people = (People) applicationContext.getBean("myBean");
    System.out.println(people);//People{name='张三'}
  }

bean的作用域

在spring里面可以设置bean实例是单实例还是多实例

默认情况下,bean是单实例

 <bean id="book" class="com.sixa.spring5.Book"></bean>

测试

  @Test
  public void test4(){
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");//加载配置文件的时候就会创建单实例对象
    Book book1 = (Book)applicationContext.getBean("book");
    Book book2 = (Book)applicationContext.getBean("book");
    System.out.println(book1.hashCode()); //1765250898
    System.out.println(book2.hashCode()); //1765250898
  }

在bean标签里,可以使用scop属性来配置该bean是单例还是多例,singleton(单例,默认)、prototype(多例)

    <bean id="book" class="com.sixa.spring5.Book" scope="prototype"></bean>

测试

  @Test
  public void test4(){
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");//加载配置文件的时候不会创建对象
    Book book1 = (Book)applicationContext.getBean("book");//getBean时创建多实例对象
    Book book2 = (Book)applicationContext.getBean("book");
    System.out.println(book1.hashCode()); //1765250898
    System.out.println(book2.hashCode()); //670971910
  }

bean的生命周期

  1. 通过构造器创建bean实例(无参构造)
  2. 为bean的属性设置值和对其他bean的引用(set)
  3. 把bean实例传递给bean后置处理器的方法postProcessBeforeInitialization
  4. 调用bean的初始化的方法(需要配置初始化的方法)
  5. 把bean实例传递给bean后置处理器的方法postProcessAfterInitialization
  6. bean可以使用了(对象获取到了)
  7. 当容器关闭时,调用bean的销毁的方法(需要配置销毁的方法)

Order类

public class Order {
  private String name;

  public Order(){
    System.out.println("第一步,调用构造函数");
  }

  public void setName(String name) {
    this.name = name;
    System.out.println("第二步,调用set方法设置值");
  }

  public void init(){
    System.out.println("第三步,调用初始化方法");
  }

  public void destroy(){
    System.out.println("第五步,调用销毁方法");
  }

  @Override
  public String toString() {
    return "Order{" +
            "name='" + name + '\'' +
            '}';
  }
}

后置处理器,MyBeanPost类,实现BeanPostProcessor接口

public class MyBeanPost implements BeanPostProcessor {
  @Override
  public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    System.out.println("bean初始化之前执行的方法");
    return bean;
  }

  @Override
  public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    System.out.println("bean初始化之后执行的方法");
    return bean;
  }
}

bean1.xml配置


    <bean id="order" class="com.sixa.spring5.Order" init-method="init" destroy-method="destroy">
        <property name="name" value="手机"></property>
    </bean>
    <!-- 配置后置处理器 -->
    <bean id="myBeanPost" class="com.sixa.spring5.MyBeanPost"></bean>

测试

  @Test
  public void test6(){
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
    Order order = (Order) applicationContext.getBean("order");
    System.out.println("第四步,获取对象");
    System.out.println(order);
    //关闭容器
    ((ClassPathXmlApplicationContext)applicationContext).close();
  }
//输出结果
/**
第一步,调用构造函数
第二步,调用set方法设置值
bean初始化之前执行的方法
第三步,调用初始化方法
bean初始化之后执行的方法
第四步,获取对象
Order{name='手机'}
第五步,调用销毁方法
**/

xml自动装配

根据指定的装配规则(属性名称或属性类型),spring自动将匹配的属性值进行注入

通过名字注入byName

Dept类

public class Dept {
  private String name;

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

  @Override
  public String toString() {
    return "Dept{" +
            "name='" + name + '\'' +
            '}';
  }
}

Emp类

public class Emp {
  private Dept dept;//必须和注入bean的id一致

  public void setDept(Dept dept) {
    this.dept = dept;
  }

  @Override
  public String toString() {
    return "Emp{" +
            "dept=" + dept +
            '}';
  }
}

bean1.xml配置

    <bean id="emp" class="com.sixa.spring5.Emp" autowire="byName"></bean>

    <!-- id必须和类的属性值一致 -->
    <bean id="dept" class="com.sixa.spring5.Dept">
        <property name="name" value="开发部"></property>
    </bean>

测试

  @Test
  public void test7(){
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean1.xml");
    Emp emp = (Emp)applicationContext.getBean("emp");
    System.out.println(emp);  //Emp{dept=Dept{name='开发部'}}
  }

通过类型注入byType

    <bean id="emp" class="com.sixa.spring5.Emp" autowire="byType"></bean>

	<!-- 相同类型的bean只能有一个 -->
    <bean id="dept" class="com.sixa.spring5.Dept">
        <property name="name" value="开发部"></property>
    </bean>

AOP

###概述

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

底层原理

AOP底层使用动态代理

有两种情况的动态代理

  1. 有接口的动态代理:JDK动态代理
  2. 没有接口的动态代理:CGLIB动态代理

JDK动态代理

UserDao接口

public interface UserDao {
  int add(int a,int b);
  String update();
}

UserDaoImp类,实现UserDao接口

public class UserDaoImp implements UserDao {

  @Override
  public int add(int a, int b) {
    System.out.println("add方法执行了");
    return a + b;
  }

  @Override
  public String update() {
    System.out.println("update方法执行了");
    return "update";
  }
}

实现JDK动态代理

public class JDKProxy {
  public static void main(String[] args) {
    Class[] userDaoClass = {UserDao.class};
    UserDao userDao = new UserDaoImp();
    UserDao userDaoProxy = (UserDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), userDaoClass, new UserDaoProxy(userDao));
    int result = userDaoProxy.add(1, 2);
    System.out.println(result);
    /**
    方法执行前。。。。。。。。addargs:[1, 2]
    add方法执行了
    方法执行后
    3
    **/
  }
}
class UserDaoProxy implements InvocationHandler{

  private Object obj;

  public UserDaoProxy(Object obj){
    this.obj = obj;
  }

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

    //方法执行前
    System.out.println("方法执行前。。。。。。。。"+method.getName()+"args:"+ Arrays.toString(args));
    //被增强的方法
    Object res = method.invoke(obj,args);
    //方法执行后
    System.out.println("方法执行后");
    return res;
  }
}

AOP术语

  1. 连接点:可以被增强的方法叫做连接点

  2. 切入点:实际被真正增强的方法叫做切入点

  3. 通知(增强):实际被增强的部分叫做通知(增强)

    五种通知类型:前置通知,后置通知,环绕通知,异常通知,最终通知

  4. 切面:把通知(增强)应用到切入点的过程叫做切面

AOP结合AspectJ

导入依赖

        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>2.2</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/aopalliance/aopalliance -->
        <dependency>
            <groupId>aopalliance</groupId>
            <artifactId>aopalliance</artifactId>
            <version>1.0</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.2.6.RELEASE</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.6.4</version>
        </dependency>

切入点表达式

作用:知道对哪个类中的哪个方法进行增强

语法结构:

execution([权限修饰符][返回类型][类全路径]方法名称)

AOP操作(AspectJ注解)

User1类(被增强类)

@Component
public class User1 {
  public void add(){
    System.out.println("add...............");
  }
}

User1Proxy类(增强类)

@Component
@Aspect
public class User1Proxy {
  //前置通知
  @Before("execution(* com.sixa.spring5.aopanno.User1.add(..))")
  public void before(){
    System.out.println("before............");
  }

  //后置通知(返回通知),被增强方法有异常不执行
  @AfterReturning("execution(* com.sixa.spring5.aopanno.User1.add(..))")
  public void afterReturn(){
    System.out.println("afterReturning............"); 
  }
  //环绕通知
  @Around("execution(* com.sixa.spring5.aopanno.User1.add(..))")
  public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
    System.out.println("around前置............");
    proceedingJoinPoint.proceed();
    System.out.println("around后置............");   //被增强方法有异常不执行
  }

  //后置通知
  @After("execution(* com.sixa.spring5.aopanno.User1.add(..))")
  public void after(){
    System.out.println("after............");
  }

  //异常通知,被增强方法有异常执行
  @AfterThrowing("execution(* com.sixa.spring5.aopanno.User1.add(..))")
  public void afterThrowing(){
    System.out.println("afterThrowing............");
  }
}

配置类

@Configuration
@ComponentScan(basePackages = "com.sixa.spring5")
@EnableAspectJAutoProxy
public class SpringConfig {

}

测试

  @Test
  public void test10(){
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
    User1 user1 = (User1) applicationContext.getBean("user1");
    user1.add(); 
    /**
    around前置............
    before............
    add...............
    around后置............
    after............
    afterReturning............
    **/
  }

抽取公共切入点

  //抽取公共切入点
  @Pointcut("execution(* com.sixa.spring5.aopanno.User1.add(..))")
  public void joinPoint(){
    
  }
  //前置通知
  @Before("joinPoint()")
  public void before(){
    System.out.println("before............");
  }

如果有两个增强类对被增强类的同一个方法进行增强,可以用@Order注解设置优先级

PersonProxy类

@Component
@Aspect
@Order(1)
public class PersonProxy {
  @After("execution(* com.sixa.spring5.aopanno.User1.add(..))")
  public void after(){
    System.out.println("person after............");
  }
}

User1Proxy类

@Component
@Aspect
@Order(2)
public class User1Proxy {
  //抽取公共切入点
  @Pointcut(value = "execution(* com.sixa.spring5.aopanno.User1.add(..))")
  public void myPoint(){

  }
  //前置通知
  @Before(value = "myPoint()")
  public void before(){
    System.out.println("before............");
  }

  //后置通知(返回通知),被增强方法有异常不执行
  @AfterReturning("execution(* com.sixa.spring5.aopanno.User1.add(..))")
  public void afterReturn(){
    System.out.println("afterReturning............");
  }
  //环绕通知
  @Around("execution(* com.sixa.spring5.aopanno.User1.add(..))")
  public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
    System.out.println("around前置............");
    proceedingJoinPoint.proceed();
    System.out.println("around后置............");   //被增强方法有异常不执行
  }

  //后置通知
  @After("execution(* com.sixa.spring5.aopanno.User1.add(..))")
  public void after(){
    System.out.println("after............");
  }

  //异常通知,被增强方法有异常执行
  @AfterThrowing("execution(* com.sixa.spring5.aopanno.User1.add(..))")
  public void afterThrowing(){
    System.out.println("afterThrowing............");
  }
}

AOP操作(AspectJ XML配置)

Book类(被增强类)

public class Book {
  public void buy(){
    System.out.println("buy............");
  }
}

BookProxy类(增强类)

public class BookProxy {
  public void before(){
    System.out.println("before..........");
  }
}

bean3.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 https://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean id="book" class="com.sixa.spring5.aopxml.Book"></bean>
    <bean id="bookProxy" class="com.sixa.spring5.aopxml.BookProxy"></bean>

    <aop:config>
        <aop:pointcut id="p" expression="execution(* com.sixa.spring5.aopxml.Book.buy(..))"></aop:pointcut>

        <aop:aspect ref="bookProxy">
            <aop:before method="before" pointcut-ref="p"></aop:before>
        </aop:aspect>
    </aop:config>

</beans>

测试

  @Test
  public void test11(){
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean3.xml");
    Book book = (Book)applicationContext.getBean("book");
    book.buy();
    /**
    before..........
    buy............
    **/
  }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值