Spring5 IOC与AOP使用详解
一、Spring功能模块划分图
二、初识IOC
1、什么是IOC?
IOC(Inversion Of Control),即“控制反转”,不是什么技术,而是一种设计思想。在Java开发中,IOC意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。如何理解好IOC呢?理解好IOC的关键是要明确“谁控制谁,控制什么,为何是反转?有反转就应该有正转,哪些方面反转了。”
谁控制谁,控制什么:传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;而IOC是有专门一个容器来创建这些对象,即由IOC容器来控制对象的创建;谁控制谁?当然是IOC 容器控制了对象;控制什么?那就是主要控制了外部资源获取(不只是包括对象,还包括其他资源,比如文件等)。
为何是反转,哪些方面反转了:有反转就有正转,传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象。为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转。哪些方面反转了?依赖对象的获取被反转了。
2、IOC底层使用技术
(1)xml配置文件
(2)dom4j解析xml(Java的XML API,是jdom的升级品,用来读写XML文件)
(3)工厂设计模式
(4)反射
3、IOC优点
- 资源集中管理,实现资源的可配置和易管理。
- 降低了使用资源双方的依赖程度,也就是我们说的耦合度。
三、BeanFactory工厂方式实现解耦(IOC核心)
/**
* 一个创建Bean对象的工厂
*
* Bean:在计算机英语中,有可重用组件的含义。
* JavaBean:用java语言编写的可重用组件。
* javabean > 实体类
*
* 它就是创建我们的service和dao对象的。
*
* 第一个:需要一个配置文件来配置我们的service和dao
* 配置的内容:唯一标识=全限定类名(key=value)
* 第二个:通过读取配置文件中配置的内容,反射创建对象
*
* 我的配置文件可以是xml也可以是properties
*/
public class BeanFactory {
//定义一个Properties对象
private static Properties props;
//定义一个Map,用于存放我们要创建的对象。我们把它称之为容器
private static Map<String,Object> beans;
//使用静态代码块为Properties对象赋值
static {
try {
//实例化对象
props = new Properties();
//获取properties文件的流对象
InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
props.load(in);
//实例化容器
beans = new HashMap<String,Object>();
//取出配置文件中所有的Key
Enumeration keys = props.keys();
//遍历枚举
while (keys.hasMoreElements()){
//取出每个Key
String key = keys.nextElement().toString();
//根据key获取value
String beanPath = props.getProperty(key);
//反射创建对象
Object value = Class.forName(beanPath).newInstance();
//把key和value存入容器中
beans.put(key,value);
}
}catch(Exception e){
throw new ExceptionInInitializerError("初始化properties失败!");
}
}
/**
* 根据bean的名称获取对象
* @param beanName
* @return
*/
public static Object getBean(String beanName){
return beans.get(beanName);
}
/**
* 根据Bean的名称获取bean对象
* @param beanName
* @return
public static Object getBean(String beanName){
Object bean = null;
try {
String beanPath = props.getProperty(beanName);
// System.out.println(beanPath);
bean = Class.forName(beanPath).newInstance();//每次都会调用默认构造函数创建对象
}catch (Exception e){
e.printStackTrace();
}
return bean;
}*/
}
四、核心容器的两个接口的区别
-
ApplicationContext:
它在构建核心容器时,创建对象采用的策略是立即加载的方式(只要一读取完配置文件马上就创建配置文件中配置的对象)
-
Beanfactory:
它在构建核心容器时,创建对象采用的策略是懒加载的方式(什么时候根据id获取对象了,什么时候才真正的创建对象)
五、spring对bean的管理细节
1、创建bean的三种方式
第一种方式:使用默认构造函数创建。
在spring的配置文件中使用bean标签,配以id和class属性之后,且没有其他属性和标签时。
采用的就是默认构造函数创建bean对象,此时如果类中没有默认构造函数,则对象无法创建。
<?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">
<!--
1.常用的属性介绍
id:类唯一标识
class:类全路径(包路径)
2.创建对象时,默认执行的是无参构造方法完成对象创建
-->
<bean id="user" class="com.ycxy.domain.User"></bean>
</beans>
第二种方式: 使用普通工厂中的方法创建对象(使用某个类中的方法创建对象,并存入spring容器)
<bean id="instanceFactory" class="com.ycxy.factory.InstanceFactory"></bean>
<bean id="account" factory-bean="instanceFactory"
factory-method="getAccount"></bean>
第三种方式:使用工厂中的静态方法创建对象(使用某个类中的静态方法创建对象,并存入spring容器)
<bean id="account" class="com.ycxy.factory.StaticFactory"
factory-method="getAccount"></bean>
2、bean对象的作用范围
bean标签的scope属性:
作用:用于指定bean的作用范围
取值: 常用的就是单例的和多例的
singleton:单例的(默认值)
prototype:多例的
request:作用于web应用的请求范围
session:作用于web应用的会话范围
global-session:作用于集群环境的会话范围(全局会话范围),当不是集群环境时,它就是session
<bean id="user" class="com.ycxy.domain.User" scope="prototype"></bean>
3、bean对象的生命周期
单例对象
出生:当容器创建时对象出生
活着:只要容器还在,对象一直活着
死亡:容器销毁,对象消亡
总结:单例对象的生命周期和容器相同
多列对象
出生:当我们使用对象时spring框架为我们创建
活着:对象只要是在使用过程中就一直活着。
死亡:当对象长时间不用,且没有别的对象引用时,由Java的垃圾回收器回收。
<bean id="user" class="com.ycxy.domain.User"
scope="prototype" init-method="init" destroy-method="destroy"></bean>
六、三种注入方式
1、构造器注入(通过有参构造器注入)
使用的标签:constructor-arg
标签出现的位置:bean标签的内部
标签中的属性
type:用于指定要注入的数据的数据类型,该数据类型也是构造函数中某个或某些参数的类型
index:用于指定要注入的数据给构造函数中指定索引位置的参数赋值。索引的位置是从0开始
name:用于指定给构造函数中指定名称的参数赋值 (常用的)
以上三个用于指定给构造函数中哪个参数赋值
value:用于提供基本类型和String类型的数据
ref:用于指定其他的bean类型数据。它指的就是在spring的Ioc核心容器中出现过的bean对象
优势:
在获取bean对象时,注入数据是必须的操作,否则对象无法创建成功。
弊端:
改变了bean对象的实例化方式,使我们在创建对象时,如果用不到这些数据,也必须提供。
<bean id="user" class="com.ycxy.domain.User">
<constructor-arg name="name" value="张三"></constructor-arg>
<constructor-arg name="age" value="18"></constructor-arg>
<constructor-arg name="birthday" ref="now"></constructor-arg>
</bean>
<!-- 配置一个日期对象 -->
<bean id="now" class="java.util.Date"></bean>
2、setter注入 (更常用)
1. 普通类型注入
涉及的标签:property
出现的位置:bean标签的内部
标签的属性
name:用于指定注入时所调用的set方法名称
value:用于提供基本类型和String类型的数据
ref:用于指定其他的bean类型数据。它指的就是在spring的Ioc核心容器中出现过的bean对象
优势:
创建对象时没有明确的限制,可以直接使用默认构造函数
弊端:
如果有某个成员必须有值,则获取对象是有可能set方法没有执行。
<bean id="book" class="com.ycxy.domain.Book">
<!--特殊字符注入方式-->
<property name="name">
<value><![CDATA[<<Java核心功法>>]]></value>
</property>
<property name="price" value="101"></property>
<property name="author" value="xxx"></property>
<property name="date">
<!-- 配置一个日期对象 -->
<bean id="now" class="java.util.Date"></bean>
</property>
</bean>
2.集合类型注入
public class Student {
//数组类型
private String[] courses;
//集合类型
private List<String> list;
public void setSet(Set<String> set) {
this.set = set;
}
//set集合类型
private Set<String> set;
//Map集合类型
private Map<String, String> map;
public void setCourses(String[] courses) {
this.courses = courses;
}
public void setList(List<String> list) {
this.list = list;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
}
<?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:util="http://www.springframework.org/schema/util"
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">
<!--
1.常用的属性介绍
id:类唯一标识
class:类全路径(包路径)
2.创建对象时,默认执行的是无参构造方法完成对象创建
-->
<bean id="user" class="com.ycxy.domain.User">
<property name="id" value="1111"></property>
<!--特殊字符注入方式-->
<property name="name">
<value><![CDATA[<<张三>>]]></value>
</property>
<property name="date">
<!-- 配置一个日期对象 -->
<bean id="now" class="java.time.LocalDate">
<constructor-arg name="year" value="2022"></constructor-arg>
<constructor-arg name="month" value="4"></constructor-arg>
<constructor-arg name="dayOfMonth" value="13"></constructor-arg>
</bean>
</property>
</bean>
<!--set注入-->
<bean id="course" class="com.ycxy.collectiontype.Course">
<property name="id">
<value>1001</value>
</property>
<property name="name">
<value>软件工程</value>
</property>
</bean>
<util:list id="courseList">
<value>Java核心功法2</value>
<value>JavaWeb2</value>
</util:list>
<!--集合类型属性的配置-->
<bean id="stu" class="com.ycxy.collectiontype.Student">
<!--数组类型注入-->
<property name="courses">
<array>
<value>Java课程</value>
<value>数据库课程</value>
<value>SpringBoot</value>
</array>
</property>
<!--
list集合类型注入
方式一: 直接使用list标签
方式二: 引入util约束, 使用util:list标签
-->
<!--<property name="list">-->
<!--<list>-->
<!--<value>Java核心功法</value>-->
<!--<value>JavaWeb</value>-->
<!--</list>-->
<!--</property>-->
<property name="list" ref="courseList"></property>
<!--set集合类型注入-->
<property name="set">
<set>
<value>Java核心卷一</value>
<value>Java核心卷二</value>
</set>
</property>
<!--map集合类型注入-->
<property name="map">
<map>
<entry key="key1" value="面向对象编程"></entry>
<entry key="key2" value="计算机网络技术"></entry>
</map>
</property>
<!--list对象类型注入-->
<property name="courseList">
<list>
<ref bean="course"></ref>
<ref bean="course"></ref>
<ref bean="course"></ref>
</list>
</property>
</bean>
</beans>
3、接口注入
接口注入模式因为历史较为悠久,在很多容器中都已经得到应用。但由于其在灵活性、易用性上不如其他两种注入模式,因而在 IOC 的专题世界内并不被看好。
七、Bean的生命周期
1、 bean 生命周期
(1)通过构造器创建bean实例(无参构造方法)
(2)为bean的属性设置值和对其他bean引用(调用set方法)
(3)调用bean 的初始化方法(需要进行配置初始化的方法,init-method=“initMethod” )
(4)bean的实例创建成功,可以使用
(5)当容器关闭时,调用bean的销毁方法(需要进行配置bean的销毁方法,destroy-method=“destroy”)
2、bean 的生命周期演示(五步)
3、加入bean 的后置处理器,bean 生命周期有七步
(1)通过构造器创建bean实例(无参构造方法)
(2)为bean的属性设置值和对其他bean引用(调用set方法)
(3)把bean实例传递bean后置处理器的前置方法(执行方法:postProcessBeforeInitialization(…))
(4)调用bean 的初始化方法
(5)把bean实例传递bean后置处理器的后置方法(执行方法:postProcessAfterInitialization(…))
(6)bean的实例创建成功,可以使用
(7)当容器关闭时,调用bean的销毁方法
4、bean 的后置处理器,bean 生命周期演示
Java代码
public class Book {
private String name;
public Book(){
System.out.println("第一步, 调用无参构造方法!");
}
public void setName(String name){
this.name = name;
System.out.println("第二步, 调用set方法设置属性值...");
}
public void initMethod(){
System.out.println("第四步调用初始化方法...");
}
public void destroyMethod(){
System.out.println("第七步调用销毁方法!");
}
}
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;
}
}
public class TestMyBeanPost {
public static void main(String[] args) {
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("bean2.xml");
ac.getBean("book", Book.class);
System.out.println("第六步, 获取bean对象...");
ac.close();
}
}
xml配置
<bean id="book" class="com.ycxy.bean.Book" init-method="initMethod" destroy-method="destroyMethod">
<property name="name" value="Java面试宝典"></property>
</bean>
<bean id="myBeanPost" class="com.ycxy.bean.MyBeanPost"></bean>
运行结果
八、外部配置文件引入
1. 添加标签约束
2. 添加jdbc.properties配置文件
properties
driverClass=com.mysql.cj.jdbc.Driver
jdbcUrl=jdbc:mysql://localhost:3306/mybatis
username=root
password=123
3. xml配置文件如下
<?xml version="1.0" encoding="UTF-8"?>
<!--suppress SpringFacetInspection -->
<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">
<!--引入jdbc配置文件-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${driverClass}"></property>
<property name="url" value="${jdbcUrl}"></property>
<property name="username" value="${username}"></property>
<property name="password" value="${password}"></property>
</bean>
</beans>
九、IOC操作Bean基于注解方式
1、Spring针对Bean管理中创建对象提供的注解
- @Component
- @Service
- @Controller
- @Repository
注:上面四个注解的功能是一样的,都可以用来创建Bean实例
2、基于注解方式实现对象的创建
第一步:引入依赖
第二步:开启组件扫描
<?xml version="1.0" encoding="UTF-8"?>
<!--suppress SpringFacetInspection -->
<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">
<!--开启组件扫描
1. 如果扫描多个包, 多个包可以使用逗号分割开
2. 扫描上层目录
-->
<!--<context:component-scan base-package="com.ycxy.annotation.dao, com.ycxy.annotation.pojo"/>-->
<!--扫描包名为com.ycxy.annotation下的所有文件-->
<context:component-scan base-package="com.ycxy.annotation"/>
<!--示例1
use-default-filters="false" 表示现在不适用默认filter, 自己配置filter
context:include-filter 设置哪些内容扫描
type="annotation" expression="org.springframework.stereotype.Service"
表示只扫描该包下的所有的@Service注解
-->
<!--
<context:component-scan base-package="com.ycxy.annotation" use-default-filters="false">
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Service"/>
</context:component-scan>
-->
<!--示例2
*上面的也必须开启扫描该包下所有文件, 否则不仅仅只是不扫描@Controller注解, 其它注解也不会扫描
context:exclude-filter 设置哪些内容不进行扫描
type="annotation" expression="org.springframework.stereotype.Controller"
表示不扫描该包下的所有的@Controller注解
-->
<context:component-scan base-package="com.ycxy.annotation" use-default-filters="false">
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
</beans>
第三步:创建类,在类上面添加创建对象的注解
若不写value属性值,则默认实例id为类名称,并且第一个字母小写,如下图:(bean的id为 userService)
测试代码
import com.ycxy.annotation.service.UserService;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestSpringAnnotation {
ApplicationContext ac;
@Before
public void before(){
ac = new ClassPathXmlApplicationContext("bean3.xml");
}
@Test
public void test01(){
UserService userService = ac.getBean("userService", UserService.class);
System.out.println(userService);
userService.add();
}
}
测试结果
3、基于注解方式实现属性注入
@Autowried:根据属性类型进行自动装配
@Qualifier:根据属性名称进行注入, 配合@Autowried使用
@Resource:可以根据类型注入,可以根据名称注入
@Value:注入普通类型属性
4、完全注解开发
(1)创建配置类,代替xml配置文件
(2)测试代码
package com.ycxy.annotation;
import com.ycxy.annotation.config.SpringConfig;
import com.ycxy.annotation.service.UserService;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class TestSpringAnnotation {
ApplicationContext ac;
@Before
public void before(){
ac = new AnnotationConfigApplicationContext(SpringConfig.class);
}
@Test
public void test(){
UserService userService = ac.getBean("userService", UserService.class);
System.out.println(userService);
userService.add();
}
}
十、AOP
1、什么是AOP?
(1)面向切面编程,利用AOP可以对业务逻辑的各个部分进行隔离,从而是的业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
(2)不通过修改源代码的方式,在主干功能里面添加新功能
2、AOP底层原理
1、AOP底层使用动态代理实现
(1)有两种情况动态代理
第一种,有接口情况,使用JDK动态代理
第二种,没有接口情况,使用CGLIB动态代理
2、编写JDK动态代理代码
(1)创建接口,定义方法;
public interface IUserDao {
int add(int a, int b);
String update(String id);
}
(2)创建接口实现类,实现方法
public class UserDao implements IUserDao {
@Override
public int add(int a, int b) {
System.out.println("UserDao add...");
return a+b;
}
@Override
public String update(String id) {
System.out.println("UserDao update...");
return id;
}
}
(3)创建代理类
class UserDaoProxy implements InvocationHandler{
//创建的代理对象
private Object obj;
public UserDaoProxy(Object obj){
this.obj = obj;
}
/**
* 方法执行之前调佣, 可以针对方法添加功能
* @param method
* @param args
*/
public void before(Method method, Object[] args){
System.out.println(method.getName() + " 方法执行之前... 参数: " + Arrays.toString(args));
}
/**
* 方法执行之后调佣, 可以针对方法添加功能
* @param method
* @param args
*/
public void after(Method method, Object[] args){
System.out.println(method.getName() + " 方法执行之后...参数: " + Arrays.toString(args));
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//方法执行之前
before(method, args);
//被增强的方法
Object o = method.invoke(obj, args); //返回的参数
//方法执行之后
after(method, args);
return o;
}
}
(4)测试JDK动态代理
public class JDKProxy {
public static void main(String[] args) {
/*Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), new Class[]{UserDao.class}, (proxy, method, arg)->{
//增强逻辑
return null;
});*/
IUserDao userDao = (IUserDao)Proxy.newProxyInstance(IUserDao.class.getClassLoader(),
new Class[]{IUserDao.class}, new UserDaoProxy(new UserDao()));
System.out.println("add方法结果: " + userDao.add(100, 300));
System.out.println("------------------------");
System.out.println("update方法结果: " + userDao.update("345435"));
}
}
执行结果
3、AOP术语
-
连接点:类里面哪些方法可以被增强,这些方法称为切入点
-
切入点:实际被真正增强的方法,称为切入点
-
通知(增强)
(1) 实际增强的逻辑部分,称为通知
(2) 通知有五种类型
① 前置通知
② 后置通知
③ 环绕通知
④ 异常通知
⑤ 最终通知 -
切面:一个动作,把通知应用到切入点的过程
4、切入点表达式
1、切入点表达式作用:知道对哪个类里面的哪个方法进行增强
2、语法结构:
execution([权限修饰符] [返回类型] [类全路径] [方法名称] ([参数列表]))
举例1:对com.ycxy.service.UserService类里面的add()方法进行增强
execution(* com.ycxy.service.UserService.add(…))
举例2:对com.ycxy.service.UserService类里面的所有方法进行增强
execution(* com.ycxy.service.UserService.*(…))
举例3:对com.ycxy.service包下的所有类的所有方法进行增强
execution(* com.ycxy.service. * . *(…))
5、AOP操作
1、 基于AspectJ注解实现(重点)
(1)创建增强类,编写增强逻辑,并在类上加入@Aspect与@Component注解
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/**
* 增强的类
*/
@Aspect
@Component
@Order(1) //当多个增强类是, 数字类型值越小, 优先级越高(控制增强类的执行顺序)
public class UserProxy {
/**
* 相同切入点抽取
*/
@Pointcut("execution(* com.ycxy.pojo.User.add(..))")
public void pointdemo(){}
/**
* 前置通知, 方法执行之前执行
*/
@Before("pointdemo()")
public void before(){
System.out.println("before ...");
}
/**
* 方法返回值之后执行
*/
@AfterReturning("pointdemo()")
public void afterReturning(){
System.out.println("afterReturning ...");
}
/**
* 后置通知, 方法执行之后执行
*/
@After("pointdemo()")
public void after(){
System.out.println("after ...");
}
/**
* 方法出现异常执行, 在after之后
*/
@AfterThrowing("execution(* com.ycxy.pojo.User.add(..))")
public void afterThrowing(){
System.out.println("afterThrowing ...");
}
/**
* 环绕通知
*/
@Around("execution(* com.ycxy.pojo.User.add(..))")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕之前 ...");
//被增强的方法执行
joinPoint.proceed();
System.out.println("环绕之后 ...");
}
}
/**
* 需要被增强的类
*/
@Component
public class User {
public void add(){
System.out.println("user add ...");
}
}
(2)进行通知的配置,在spring配置中开启注解扫描,并开启@Aspect生成代理对象
① 方式一:基于XML方式进行配置
<?xml version="1.0" encoding="UTF-8"?>
<!--suppress SpringFacetInspection -->
<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">
<!--开启注解扫描-->
<context:component-scan base-package="com.ycxy"/>
<!-- 扫描@Aspect注解, 开启Aspect生成代理对象-->
<aop:aspectj-autoproxy/>
</beans>
② 方式二:基于Java注解方式代替上面XML配置文件
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@ComponentScan(basePackages = {"com.ycxy"})
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AopConfig {
}
(3)spring 整合junit进行测试
1、依赖导入
<!--spring test start -->
<!-- 单元测试Junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<!--spring test end -->
2、测试代码
import com.ycxy.pojo.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.beans.factory.annotation.Autowired;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:application.xml")
public class SpringTestAop_01 {
@Autowired
User user;
@Test
public void test01(){
user.add();
}
}
测试结果
2、 基于AspectJ配置文件实现
<bean id="user" class="com.ycxy.pojo.User"></bean>
<bean id="userProxy" class="com.ycxy.aopanno.UserProxy"></bean>
<!--配置aop增强-->
<aop:config>
<!--切入点-->
<aop:pointcut id="p" expression="execution(* com.ycxy.pojo.User.add(..))"/>
<!--配置切面-->
<aop:aspect ref="userProxy">
<!--增强作用在具体的哪个方法上-->
<aop:before method="before" pointcut-ref="p"/>
</aop:aspect>
</aop:config>