【spring5】-【IOC】-【AOP】

IOC

IOC操作Bean管理(XML方式)

工厂bean(FactoryBean)

一、Spring有两种类型bean,一种普通bean,另外一种工厂bean(FactoryBean)
二、普通bean:在配置文件中定义bean类型就是返回类型。例如在bean.xml中配置对象创建:

<bean id="book" class="com.atguigu.spring5.collectiontype.Book" >
	<property name="list" ref="bookList"></property>
</bean>

你这里定义的bean类型class="com.atguigu.spring5.collectiontype.Book"就是返回类型,即返回类型是Book类,我们之前讲的就是普通bean。
三、工厂bean:在配置文件定义bean类型可以和返回类型不一样。假如你定义的bean类型为class="com.atguigu.spring5.collectiontype.Book",即Book类,但是返回的bean类型可能是其他类型。工厂bean应该怎么做:

  1. 创建类,让这个类作为工厂bean,实现接口FactoryBean,实现接口里面的方法,在实现的方法getObject中定义返回的bean类型:新建src/com/atguigu/spring5/factorybean/MyBean.java文件,代码如下:
public class MyBean implements FactoryBean<Course> {
    //定义返回bean,工厂bean返回的类型和定义的类型不一样全是因为在这个方法中定义了返回bean的类型
    @Override
    public Course getObject() throws Exception {
        Course course = new Course();
        course.setCname("abc");
        return course;
    }

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

    @Override
    public boolean isSingleton() {
        return false;
    }
}
  1. 配置文件和普通bean的一样:<bean id="myBean" class="com.atguigu.spring5.factorybean.MyBean"></bean>
  2. 测试:TestSpring5Demo1中的代码如下:
public class TestSpring5Demo1 {
    @Test
    public void test3() {
        ApplicationContext context =
                new ClassPathXmlApplicationContext("bean3.xml");
        Course course = context.getBean("myBean", Course.class);
        System.out.println(course);
    }
}

bean的作用域

一、在Spring里面,可以设置创建的bean实例是单实例还是多实例,默认情况下,bean是单实例对象

二、如何设置单实例还是多实例:在spring配置文件bean标签里面有属性scope用于设置单实例还是多实例,常用的scope属性值:

  1. singleton:默认值,表示是单实例对象
  2. prototype:表示是多实例对象
<bean id="book" class="com.atguigu.spring5.collectiontype.Book" scope="prototype">
	<property name="list" ref="bookList"></property>
</bean>

以上测试代码输出:
在这里插入图片描述
三、singleton和prototype区别

  1. singleton单实例,prototype多实例
  2. 设置scope值是singleton时,加载spring配置文件时候就会创建单实例对象,即执行ApplicationContext context = new ClassPathXmlApplicationContext("bean3.xml");这行代码时就创建单实例对象了
    设置scope值是prototype时,不是在加载spring配置文件时候创建对象,在调用getBean方法时候创建多实例对象:Course course = context.getBean("myBean", Course.class);,执行这行代码时创建对象,每次调用时创建的对象都不一样

bean的生命周期

一、生命周期:从对象创建到对象销毁的过程
二、bean生命周期

  1. 通过构造器创建bean实例(无参数构造)
  2. 为bean的属性设置值和对其他bean引用(调用set方法)
  3. 调用bean的初始化的方法(等下我们需要进行配置初始化的方法)
  4. bean可以使用了(对象获取到了)
  5. 当容器关闭时候,调用bean的销毁的方法(等下我们需要进行配置销毁的方法)

三、演示bean生命周期

  1. 新建src/com/atguigu/spring5/bean/Orders.java,在其中定义无参构造函数、属性及属性的set方法、bean的初始化的方法、bean的销毁的方法
public class Orders {
    //无参数构造
    public Orders() {
        System.out.println("第一步 执行无参数构造创建bean实例");
    }

    private String oname;
    public void setOname(String oname) {
        this.oname = oname;
        System.out.println("第二步 调用set方法设置属性值");
    }

    //创建执行的初始化的方法
    public void initMethod() {
        System.out.println("第三步 执行初始化的方法");
    }

    //创建执行的销毁的方法
    public void destroyMethod() {
        System.out.println("第五步 执行销毁的方法");
    }
}
  1. 新建src/bean4.xml文件,在其中进行配置,注意使用bean标签的init-method属性指定bean的初始化方法initMethoddestroy-method属性指定bean的销毁方法destroyMethod
<bean id="orders" class="com.atguigu.spring5.bean.Orders" init-method="initMethod" destroy-method="destroyMethod">
	<property name="oname" value="手机"></property>
</bean>
  1. 测试:TestSpring5Demo1中代码如下:
public class TestSpring5Demo1 {
    @Test
    public void testBean3() {
        ApplicationContext context =
                new ClassPathXmlApplicationContext("bean4.xml");
        Orders orders = context.getBean("orders", Orders.class);
        System.out.println("第四步 获取创建bean实例对象");
        System.out.println(orders);

        //手动让bean实例销毁
        ((ClassPathXmlApplicationContext)context).close();
    }
}

在销毁bean实例时做了个强转((ClassPathXmlApplicationContext)context).close();,如果不想做这个强转,可以这样写:

public class TestSpring5Demo1 {
    @Test
    public void testBean3() {
        ClassPathXmlApplicationContext context =
                new ClassPathXmlApplicationContext("bean4.xml");
        Orders orders = context.getBean("orders", Orders.class);
        System.out.println("第四步 获取创建bean实例对象");
        System.out.println(orders);
        //手动让bean实例销毁
        context.close();
    }
}

ClassPathXmlApplicationContextApplicationContext的子接口,在这里可以使用前者代替后者
在这里插入图片描述
四、加上bean的后置处理器,bean的生命周期有七步:

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

五、演示添加后置处理器效果

  1. 创建src/com/atguigu/spring5/bean/MyBeanPost.java类,实现接口BeanPostProcessor,创建后置处理器:
public class MyBeanPost implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("在初始化之前执行的方法");
        return bean;
    }
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("在初始化之后执行的方法");
        return bean;
    }
}
  1. bean4.xml中配置后置处理器:
<bean id="orders" class="com.atguigu.spring5.bean.Orders" init-method="initMethod" destroy-method="destroyMethod">
	<property name="oname" value="手机"></property>
</bean>

<!--配置后置处理器-->
<bean id="myBeanPost" class="com.atguigu.spring5.bean.MyBeanPost"></bean>

加载配置文件时,他会创建对象和后置处理器,只要类实现了接口BeanPostProcessor,spring就会把他作为后置处理器执行,后置处理器会给当前配置文件中的所有bean实例添加后置处理器。执行前面的测试代码后输出:
在这里插入图片描述

xml自动装配

一、自动装配:根据指定装配规则(属性名称或者属性类型),Spring自动将匹配的属性值进行注入。我们前面向一个类中注入属性时,在配置文件中做如下配置:

<bean id="orders" class="com.atguigu.spring5.bean.Orders" >
	<property name="oname" value="手机"></property>
</bean>

我们是通过bean标签中的property标签的name属性、value属性或ref属性注入属性的,也就是你手动指定哪个属性注入哪个值,这种方式叫做手动装配
二、演示自动装配过程

  1. 新建src/com/atguigu/spring5/autowire文件夹,在其中新建两个类Dept.javaEmp.java,代码分别如下:
public class Dept {
    @Override
    public String toString() {
        return "Dept{}";
    }
}

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

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

    public void test() {
        System.out.println(dept);
    }
}
  1. 新建src/bean5.xml配置文件,在其中进行自动配置,bean标签中有个属性autowire,专门用来配置自动装配。autowire属性常用两个值:
    (1)byName根据属性名称注入 ,注入值beanid值和类属性名称一样,下面就完成了Emp类中的dept属性注入
<bean id="emp" class="com.atguigu.spring5.autowire.Emp" autowire="byName"></bean>
<bean id="dept" class="com.atguigu.spring5.autowire.Dept"></bean>

说明:Emp类的dept属性名和<bean id="dept" class="com.atguigu.spring5.autowire.Dept"></bean>beanid值一致
(2)byType根据属性类型注入

<bean id="emp" class="com.atguigu.spring5.autowire.Emp" autowire="byType"></bean>
<bean id="dept" class="com.atguigu.spring5.autowire.Dept"></bean>

说明:Emp类的dept属性的类型Dept类,spring会在配置文件中找到Dept类的对象<bean id="dept" class="com.atguigu.spring5.autowire.Dept"></bean>进行注入
注意:在使用byType根据属性类型注入时,如果类型中有重复的对象,如下面有两个Dept类的对象,此时spring压根不知道使用dept还是dept1,会报错

<bean id="emp" class="com.atguigu.spring5.autowire.Emp" autowire="byType"></bean>
<bean id="dept" class="com.atguigu.spring5.autowire.Dept"></bean>
<bean id="dept1" class="com.atguigu.spring5.autowire.Dept"></bean>

测试:TestSpring5Demo1.java中代码如下:

public class TestSpring5Demo1 {
    @Test
    public void test4() {
        ApplicationContext context =
                new ClassPathXmlApplicationContext("bean5.xml");
        Emp emp = context.getBean("emp", Emp.class);
        System.out.println(emp);
    }
}

引入外部属性文件

一、我们之前进行属性注入时,在配置文件中一般做如下配置,有多少个属性就要写多少个property标签,当类发生变化时,还需要改配置文件,那么我们可以把相关值、固定值放在一个其他文件中,然后将其他文件引入配置文件,配置文件读取其他文件的内容

<bean id="orders" class="com.atguigu.spring5.bean.Orders" >
	<property name="oname" value="手机"></property>
</bean>

二、以数据库配置文件为例

  1. 直接配置数据库信息:
    (1)引入连接池依赖jar包:将下列jar包复制到lib文件夹下
    在这里插入图片描述
    引入进来:
    在这里插入图片描述
    在这里插入图片描述
    新建src/bean6.xml配置文件,在其中直接配置连接池:
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
	<!--数据库驱动-->
	<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
	<!--数据库地址-->
	<property name="url" value="jdbc:mysql://localhost:3306/userDb"></property>
	<!--连接数据库的用户名-->
	<property name="username" value="root"></property>
	<!--数据库密码-->
	<property name="password" value="root"></property>
</bean>
  1. 引入外部属性文件配置数据库连接池
    (1)创建外部属性文件src/jdbc.properties(注意要是properties格式文件),写数据库信息
// 为了防止冲突,属性名都加前缀prop.
prop.driverClass=com.mysql.jdbc.Driver
prop.url=jdbc:mysql://localhost:3306/userDb
prop.userName=root
prop.password=root

(2)把外部properties属性文件引入到spring配置文件中
首先引入context名称空间
在这里插入图片描述
然后使用context:property-placeholder标签引入外部属性文件,context:property-placeholder有一个location属性,用来指定要引入的外部文件的路径,因为jdbc.propertiessrc文件夹下,所以可以写作classpath:jdbc.properties。最后使用${外部文件的键名}表达式配置连接池

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

<!--配置连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
	<property name="driverClassName" value="${prop.driverClass}"></property>
	<property name="url" value="${prop.url}"></property>
	<property name="username" value="${prop.userName}"></property>
	<property name="password" value="${prop.password}"></property>
</bean>

IOC操作Bean管理(注解方式)

创建对象

一、什么是注解

  1. 注解是代码特殊标记,格式:@注解名称(属性名称=属性值, 属性名称=属性值..)
  2. 使用注解,注解作用在类上面,方法上面,属性上面
  3. 使用注解目的:简化xml配置

二、Spring针对Bean管理中创建对象提供注解,下面四个注解功能是一样的,都可以用来创建bean实例

  1. @Component:spring容器中提供的普通组件
  2. @Service:用于业务逻辑层或service层
  3. @Controller:用于web层
  4. @Repository:用于dao层或持久层

三、基于注解方式实现对象创建,新建项目spring5_demo3

  1. 引入依赖:将下面这个jar包复制到lib文件夹下
    在这里插入图片描述
    在项目中导入:
    在这里插入图片描述
  2. 开启组件扫描,也就是告诉spring我现在在哪个类中要加上注解,让spring去扫描这个类,看类上面有没有注解,有注解就创建对象,即指定spring要扫描的位置
    (1)在配置文件中引入名称空间
    在这里插入图片描述
    (2)开启组件扫描:使用context:component-scan标签的base-package属性指定扫描哪个包下的内容,其属性值是包名:<context:component-scan base-package="com.atguigu.spring5.dao"></context:component-scan>
    如果扫描多个包,多个包使用逗号隔开:<context:component-scan base-package="com.atguigu.spring5.dao, com.atguigu.spring5.service"></context:component-scan>。或者扫描多个包的上层目录,即他们的共同的父目录:<context:component-scan base-package="com.atguigu.spring5"></context:component-scan>
    (3)创建类,在类上面添加创建对象注解@Component(value = "这个组件的标识名,相当于bean标签的id属性值")value属性值可以省略不写,省略不写使用默认值,默认值是首字母小写的类名称UserService -- userService
@Component(value = "userService")  //<bean id="userService" class=".."/>
public class UserService {
    public void add() {
        System.out.println("service add......."+name);
        userDao.add();
    }
}
  1. 测试:新建src/com/atguigu/spring5/testdemo/TestSpring5Demo1.java文件,测试代码:
public class TestSpring5Demo1 {
    @Test
    public void testService1() {
        ApplicationContext context
                = new ClassPathXmlApplicationContext("bean1.xml");
        UserService userService = context.getBean("userService", UserService.class);
        System.out.println(userService);
        userService.add();
    }
}

四、基于注解方式实现对象创建执行过程:

  1. 加载配置文件ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
  2. 配置文件中只开启了组件扫描:<context:component-scan base-package="com.atguigu"></context:component-scan>,这句话一生效,spring就知道你要用注解方式实现对象创建,他就去你指定的包中找到包中的所有类,若类上面有相关的注解,他就根据注解创建对象。
  3. UserService userService = context.getBean("userService", UserService.class);对象创建后调用包做输出
组件扫描配置

之前我们使用<context:component-scan base-package="com.atguigu"></context:component-scan>这种配置,spring会扫描com.atguigu包下的所有类,但是有些类是没有必要扫的,那么我们可以对组件扫描进行更精细化的配置。

  1. context:component-scan标签设置use-default-filters="false"属性,表示现在不使用默认filter,自己配置filter,即不扫描com.atguigu包下的所有类,扫描com.atguigu包下我们指定的类。context:include-filter标签用于设置扫描哪些内容,他的type属性表明是注解扫描,expression属性用于指定扫描哪些注解
<!--只扫描com.atguigu包中带Controller注解的类-->
<context:component-scan base-package="com.atguigu" use-default-filters="false">
	<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
  1. 使用<context:component-scan base-package="com.atguigu"></context:component-scan>配置扫描包所有内容,然后在context:component-scan标签中使用context:exclude-filter标签设置哪些内容不进行扫描
<!--不扫描com.atguigu包中带Controller注解的类-->
<context:component-scan base-package="com.atguigu">
	<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

属性注入

新建src/com/atguigu/spring5/dao文件夹,在其中创建UserDao.java及其实现类UserDaoImpl.java,代码如下:

public interface UserDao {
    public void add();
}

public class UserDaoImpl implements UserDao {
    @Override
    public void add() {
        System.out.println("dao add.....");
    }
}
@Autowired:根据属性类型进行自动装配

我们现在想实现在service中调用dao中的方法:

  1. 创建servicedao对象,在servicedao类添加创建对象注解:在service的类上添加@Service注解,在UserDaoImpl上添加@Repository注解
  2. service注入dao对象,在service类添加dao类型属性,在属性上面使用注解
    (1)在属性上面使用@Autowired根据类型进行注入:
@Service
public class UserService {
    //定义dao类型属性,不需要添加set方法,添加注入属性注解
    @Autowired  //根据类型进行注入
    private UserDao userDao;

    public void add() {
        System.out.println("service add......."+name);
        userDao.add();
    }
}
@Qualifier:根据名称进行注入

一个类型有很多个实现类,此时用@Autowired根据类型注入就没法注入,spring不知道用哪个实现类,现在我们使用@Qualifier根据名称进行注入:@Qualifier(value = "实现类的注入名称")

  1. UserDaoImpl类上使用@Repository注解设置组件标识名,相当于bean标签的id属性值,不设置则使用默认值,默认值是首字母小写的类名称
@Repository(value = "userDaoImpl1")
public class UserDaoImpl implements UserDao {
    @Override
    public void add() {
        System.out.println("dao add.....");
    }
}
  1. UserService中使用@Qualifier注解进行注入,在@Qualifier中使用value属性指定组件标识名,也就是说@Qualifier中的value属性值和@Repository中的value属性值一致,代码如下:
@Service
public class UserService {
    //定义dao类型属性,不需要添加set方法,添加注入属性注解
    @Qualifier(value = "userDaoImpl1") //根据名称进行注入
    private UserDao userDao;

    public void add() {
        System.out.println("service add......."+name);
        userDao.add();
    }
}
@Resource:可以根据类型注入,可以根据名称注入

@Resource是Java扩展包javax的,不是spring的,其余都是spring的

@Service
public class UserService {  
    // @Resource  //根据类型进行注入
    @Resource(name = "userDaoImpl1")  //根据名称进行注入
    private UserDao userDao;

    public void add() {
        System.out.println("service add......."+name);
        userDao.add();
    }
}
@Value:注入普通类型属性
@Service
public class UserService {
    @Value(value = "abc")// 将"abc"注入到name中,使得name = “abc”
    private String name;

    public void add() {
        System.out.println("service add......."+name);
        userDao.add();
    }
}

完全注解开发

纯注解开发:配置文件bean1.xml可以没有,可以把里面的内容<context:component-scan base-package="com.atguigu"></context:component-scan>放在其他地方,用完全注解的方式开发:

  1. 创建配置类src/com/atguigu/spring5/config/SpringConfig.java,替代xml配置文件
@Configuration  //这个注解会把当前类作为配置类,替代xml配置文件
@ComponentScan(basePackages = {"com.atguigu"})// @ComponentScan开启组件扫描,basePackages指定扫描的包的路径,如果有多个包用“,”隔开
public class SpringConfig {}
  1. 编写测试类,使用AnnotationConfigApplicationContext(配置类)加载注解配置类
public class TestSpring5Demo1 {
    @Test
    public void testService2() {
        // 加载配置类
		// AnnotationConfigApplicationContext:注解配置类
        ApplicationContext context
                = new AnnotationConfigApplicationContext(SpringConfig.class);
        UserService userService = context.getBean("userService", UserService.class);
        System.out.println(userService);
        userService.add();
    }
}

AOP

什么是 AOP:

  1. 面向切面编程(方面),利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
  2. 通俗描述:不通过修改源代码方式,在主干功能里面添加新功能
  3. 使用登录例子说明AOP
    在这里插入图片描述
    登录流程:用户输入用户名密码后,我们去数据库中查询,判断用户名密码是否正确,不正确则重新输入,正确则登陆成功显示主页面。现在我们想在登陆成功后进行权限判断,如果你是管理员我给你显示管理员的页面,如果你是普通用户我给你显示普通用户的页面,如果是以前的话,需要修改原来的代码,但是AOP编程逻辑是新增一个权限控制模块,将其添加到登陆成功但是还没由展示页面这一过程中,以后如果不需要这个功能了,直接将模块移除即可,不会对原来的代码造成很大的影响

底层原理

一、AOP底层使用动态代理

  1. 有两种情况动态代理
    (1)有接口情况,使用JDK动态代理,通过创建接口实现类代理对象来增强类的方法
    在这里插入图片描述
    (2)没有接口情况,使用CGLIB动态代理,通过创建当前类的子类作为代理对象(也就是说这个子类是代理对象)来增强类的方法
    在这里插入图片描述

JDK动态代理

一、使用JDK动态代理,使用Proxy类(java.lang.reflect.Proxy)里面的方法创建代理对象

  1. Proxy类中有一个方法调用newProxyInstance方法来实现JDK动态代理
    在这里插入图片描述
    方法有三个参数:
    (1)类加载器
    (2)增强方法所在的类,这个类实现的接口,支持多个接口
    (3)实现这个接口InvocationHandler,创建代理对象,写增强的部分

二、编写JDK动态代理代码

  1. 创建接口,定义方法
public interface UserDao {
 	public int add(int a,int b);
 	public String update(String id);
}
  1. 创建接口实现类,实现方法
public class UserDaoImpl implements UserDao {
    @Override
    public int add(int a, int b) {
        System.out.println("add方法执行了.....");
        return a+b;
    }

    @Override
    public String update(String id) {
        System.out.println("update方法执行了.....");
        return id;
    }
}
  1. 接下来通过动态代理的方式把add方法、update方法中的功能进行增强(增强的意思就是往里面加一些逻辑,加一些功能):新建src/com/atguigu/spring5/JDKProxy.java文件,在里面使用Proxy类创建接口代理对象
public class JDKProxy {
    public static void main(String[] args) {
        //创建接口实现类代理对象
        Class[] interfaces = {UserDao.class};
        // 第三个参数可以通过匿名内部类的方式传入对象
//        Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new InvocationHandler() {
//            @Override
//            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//                return null;
//            }
//        });
		// 不适用匿名内部类
        UserDaoImpl userDao = new UserDaoImpl();
        UserDao dao = (UserDao)Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UserDaoProxy(userDao));
        int result = dao.add(1, 2);
        System.out.println("result:"+result);
    }
}

//创建代理对象代码
class UserDaoProxy implements InvocationHandler {
    //1 创建的是谁的代理对象,把谁传递过来
    //通过有参数构造传递,建的是谁的代理对象,把谁作为参数传给有参构造方法,这里应该传UserDaoImpl,即public UserDaoProxy(UserDaoImpl userDaoImpl)
    // 但是由于我们希望这个代理对象更通用,所以传入的是Object,即:public UserDaoProxy(Object obj)
    private Object obj;// 代理对象
    public UserDaoProxy(Object obj) {
        this.obj = obj;
    }

    //只要UserDaoProxy对象一创建,invoke方法就会被调用,在这个方法中写增强的逻辑。
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //被增强的方法之前执行 method.getName()获得方法的名字
        System.out.println("方法之前执行...."+method.getName()+" :传递的参数..."+ Arrays.toString(args));

        //被增强的方法执行,这个方法就是你原来写的方法,是UserDaoImpl中的add,update方法
        // method中有个invoke方法,invoke方法接受两个参数,第一个是代理对象,第二个是参数
        Object res = method.invoke(obj, args);

        //被增强的方法之后执行
        System.out.println("方法之后执行...."+obj);
        return res;
    }
}

AOP操作术语

一、连接点:类里面哪些方法可以被增强,这些方法称为连接点
二、切入点:实际被真正增强的方法,这些方法称为切入点
三、通知(增强):实际增强的逻辑部分称为通知(增强)。通知有多种类型:

  1. 前置通知:增强方法执行之前执行的方法
  2. 后置通知:增强方法执行之后执行的方法
  3. 环绕通知:增强方法执行之前、之后执行的方法
  4. 异常通知:增强方法执行异常时执行的方法
  5. 最终通知:不管增强方法是否执行成功,是否有异常都执行的方法

四、切面:使一个动作,把通知应用到切入点这一过程,如把权限判断加到登录流程这一过程就是切面

准备工作

一、Spring框架一般都是基于AspectJ实现AOP操作。AspectJ不是Spring组成部分,而是一个独立AOP框架,一般把AspectJ和Spirng框架一起使用,进行AOP操作
二、基于AspectJ实现AOP操作有两种方式:

  1. 基于xml配置文件实现
  2. 基于注解方式实现(使用)

三、在项目工程里面引入AOP相关依赖

  1. 将以下4个jar包复制到lib文件中
    在这里插入图片描述
    在这里插入图片描述
  2. 将上述4个jar包导入到项目中
    在这里插入图片描述
    四、切入点表达式
  3. 切入点表达式作用:知道对哪个类里面的哪个方法进行增强
  4. 语法结构: execution([权限修饰符] [返回类型] [类全路径] [方法名称]([参数列表]) )
    (1)权限修饰符可以为*,代表任意修饰符
    (2)返回类型可以省略
    (3)参数列表中的..表示是方法中的参数
  1. com.atguigu.dao.BookDao类里面的add进行增强:execution(* com.atguigu.dao.BookDao.add(..))
  2. com.atguigu.dao.BookDao类里面的所有的方法进行增强:execution(* com.atguigu.dao.BookDao.* (..))
  3. 对com.atguigu.dao包里面所有类,类里面所有方法进行增强:execution(* com.atguigu.dao.*.* (..))

基于AspectJ注解实现AOP操作

一、创建类src/com/atguigu/spring5/aopanno/User.java,在类里面定义方法

//被增强的类
public class User {
	public void add() {
		System.out.println("add.......");
	}
}

二、创建增强类(编写增强逻辑)
(1)新建src/com/atguigu/spring5/aopanno/UserProxy.java增强类,在里面创建方法,让不同方法代表不同通知类型

//增强的类
public class UserProxy {
	public void before() {//前置通知
		System.out.println("before......");
	}
	// 后置通知等通知都可以在后面配置,这里暂不做演示
}

三、进行通知的配置

  1. 在spring配置文件中,开启注解扫描
    (1)配置文件中引入命名空间:
    在这里插入图片描述
    (2)开启注解扫描
<context:component-scan base-package="com.atguigu.spring5.aopanno"></context:component-scan>
  1. 使用注解创建UserUserProxy对象
@Component
public class User {// ...}
@Component
public class UserProxy {}
  1. 在增强类上面添加注解@Aspect,表示要给这个类生成代理对象
@Component
@Aspect  //生成代理对象
public class UserProxy {}
  1. 在spring配置文件中开启生成代理对象:<aop:aspectj-autoproxy></aop:aspectj-autoproxy>,之后spring会到注解扫描指定的包中找类上面有@Aspect注解的类,他就给这个类生成代理对象

四、配置不同类型的通知:在增强类的里面,在作为通知方法上面添加通知类型注解,并使用切入点表达式来指定要增强的方法

//增强的类
@Component
@Aspect //生成代理对象
public class UserProxy {
	//前置通知
	//@Before 注解表示作为前置通知
 	@Before(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
 	public void before() {
 		System.out.println("before.........");
 	}
 	//后置通知(返回通知)
 	@AfterReturning(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
	public void afterReturning() {
 		System.out.println("afterReturning.........");
 	}
 	//最终通知
 	@After(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
 	public void after() {
 		System.out.println("after.........");
 	}
 	//异常通知
 	@AfterThrowing(value = "execution(* 
com.atguigu.spring5.aopanno.User.add(..))")
 	public void afterThrowing() {
 		System.out.println("afterThrowing.........");
 	}
 	//环绕通知
 	@Around(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
 	public void around(ProceedingJoinPoint proceedingJoinPoint) throws 
Throwable {
 		System.out.println("环绕之前.........");
 		//被增强的方法执行
 		proceedingJoinPoint.proceed();
 		System.out.println("环绕之后.........");
 	}
}

五、测试:新建src/com/atguigu/spring5/test/TestAop.java,其中代码为:

public class TestAop {
    @Test
    public void testAopAnno() {
        ApplicationContext context =
                new ClassPathXmlApplicationContext("bean1.xml");
        User user = context.getBean("user", User.class);//我们要增强User类中的方法
        user.add();
    }
}

增强方法没有异常的执行顺序:
在这里插入图片描述
增强方法有异常的执行顺序:
在这里插入图片描述

相同的切入点抽取

上面在配置不同类型的通知时,使用了相同的切入点,即切入点表达式都一样value = "execution(* com.atguigu.spring5.aopanno.User.add(..))",那么我们把相同的切入点表达式提取出来,减少冗余代码

//增强的类
@Component
@Aspect //生成代理对象
public class UserProxy {
    //相同切入点抽取:随便写一个方法,给方法配置注解@Pointcut(value = "切入点表达式")
    @Pointcut(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
    public void pointdemo() {}
    
	//前置通知
	//@Before 注解表示作为前置通知,value的值为执行切入点方法
 	@Before(value = "pointdemo()")
 	public void before() {
 		System.out.println("before.........");
 	}
	// ...
}
有多个增强类对同一个方法进行增强,设置增强类优先级

新建src/com/atguigu/spring5/aopanno/PersonProxy.java,另外,这个类也想对User类中的add方法进行增强,而UserProxy类也对User类中的add方法进行增强,现在我想设置这两个方法的优先级:在增强类上面添加注解@Order(数字类型的值),数字类型的值从0开始越小优先级越高。PersonProxy类中代码如下:

@Component
@Aspect
@Order(1)
public class PersonProxy {
    //后置通知(返回通知)
    @Before(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
    public void afterReturning() {
        System.out.println("Person Before.........");
    }
}

User类上添加注解:@Order(3)

完全使用注解开发

创建配置类src/com/atguigu/spring5/config/ConfigAop.java,代码如下,不需要创建xml配置文件

@Configuration// 表明这是个配置类
@ComponentScan(basePackages = {"com.atguigu"})// 开启注解扫描,扫描com.atguigu这个包
@EnableAspectJAutoProxy(proxyTargetClass = true)// 开启Aspect生成代理对象,proxyTargetClass默认为false,我们要手动把他设为true
public class ConfigAop {}

基于AspectJ配置文件方式实现AOP操作

一、创建两个类,增强类src/com/atguigu/spring5/aopxml/BookProxy.java和被增强类src/com/atguigu/spring5/aopxml/Book.java,并为他们创建方法:

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

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

二、在spring配置文件src/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"
       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/context http://www.springframework.org/schema/context/spring-context.xsd
                        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--创建对象-->
    <bean id="book" class="com.atguigu.spring5.aopxml.Book"></bean>
    <bean id="bookProxy" class="com.atguigu.spring5.aopxml.BookProxy"></bean>

    <!--配置aop增强-->
    <aop:config>
        <!--切入点使用切入点表达式指定
        id:指定切入点名字,随便写
        expression:指定切入点表达式
        -->
        <aop:pointcut id="p" expression="execution(* com.atguigu.spring5.aopxml.Book.buy(..))"/>
        <!--配置切面 ref指定增强类-->
        <aop:aspect ref="bookProxy">
            <!--增强作用在具体的方法上
            aop:具体通知,如aop:before
            method:指定增强的方法
            pointcut-ref:指定增强方法要作用在哪个切入点上
            -->
            <aop:before method="before" pointcut-ref="p"/>
        </aop:aspect>
    </aop:config>
</beans>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring-IOCSpring框架的核心部分之一,它是一种设计模式,全称为Inversion of Control(控制反转)。它通过将对象的创建、依赖关系的管理和对象的生命周期交给Spring容器来实现,从而降低了组件之间的耦合度,提高了代码的可重用性和可维护性。Spring-IOC实现主要依靠Spring容器,Spring容器是Spring框架的核心,它负责创建、管理和装配Bean对象,其中Bean是Spring框架中最基本的组件。 Spring-IOC实现主要有两种方式:BeanFactory和ApplicationContext。其中,BeanFactorySpring-IOC的基本实现,而ApplicationContext是BeanFactory的子接口,提供了更多高级特性。ApplicationContext是Spring框架中最常用的IOC容器,它除了提供BeanFactory的所有功能外,还提供了更多的企业级特性,例如AOP、事务管理、国际化、事件传播等。 下面是一个简单的Spring-IOC的例子,假设我们有一个UserService接口和一个UserServiceImpl实现,我们可以通过Spring-IOC容器来创建和管理UserServiceImpl对象: 1.定义UserService接口和UserServiceImpl实现 ```java public interface UserService { void addUser(User user); } @Service public class UserServiceImpl implements UserService { @Override public void addUser(User user) { // 添加用户的具体实现 } } ``` 2.在Spring配置文件中配置UserService实例 ```xml <bean id="userService" class="com.example.service.UserServiceImpl"/> ``` 3.在代码中获取UserService实例并使用 ```java ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); UserService userService = context.getBean("userService", UserService.class); User user = new User(); userService.addUser(user); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值