AOP:面向切面编程
AOP底层实现:基于JDK动态代理和基于Cglib的动态代理
AOP的重点概念:
开发明确事项:
1.练习程序不区分Service、Dao层,都放在一个package中。
2.本例中的JKD代理方法和Cglib代理方法分别在两个package中实现
public interface TargetInterface {
void save();
}
public class Target implements TargetInterface{
@Override
public void save() {
System.out.println("save running.......");
}
}
public class Advice {
public void before(){
System.out.println("前置增强.....");
}
public void afterReturning(){
System.out.println("后置增强......");
}
}
public class ProxyTest {
public static void main(String[] args) {
//目标对象
final Target target=new Target();
//增强对象
final Advice advice=new Advice();
//newProxyInstance的返回值是动态生成的代理对象,是接口
TargetInterface proxy=(TargetInterface) Proxy.newProxyInstance(
target.getClass().getClassLoader(), //目标对象类加载器
target.getClass().getInterfaces(), //目标对象相同的接口字节码对象数组
new InvocationHandler(){
//调用代理对象的任何方法,实质执行的都是invoke方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
advice.before();
Object invoke=method.invoke(target,args); //执行目标方法
advice.afterReturning();
return invoke;
}
}
);
//调用代理对象的方法
proxy.save();
}
}
(5)运行测试类,查看是否成功
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.3</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.2</version>
</dependency>
public class Target {
public void save() {
System.out.println("save running.......");
}
}
public class Advice {
public void before(){
System.out.println("前置增强.....");
}
public void afterReturning(){
System.out.println("后置增强......");
}
}
public class ProxyTest {
public static void main(String[] args) {
//目标对象
final Target target = new Target();
//增强对象
final Advice advice = new Advice();
//返回值 动态生成的代理对象 基于Cglib
//1.创建增强器
Enhancer enhancer = new Enhancer();
//2.设置父类(目标)
enhancer.setSuperclass(Target.class);
//3.设置回调
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//执行前置
advice.before();
//执行目标
Object invoke = method.invoke(target, args);
//执行后置
advice.afterReturning();
return invoke;
}
});
//4.创建代理对象
Target proxy = (Target) enhancer.create();
proxy.save();
}
}
(5)运行测试类,查看是否成功
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.3</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>3.0.7.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
public interface TargetInterface {
void save();
}
public class Target implements TargetInterface {
@Override
public void save() {
System.out.println("save running.......");
}
}
public class MyAspect {
public void before(){
System.out.println("前置增强......");
}
}
<?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
https://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="target" class="com.lee.aop.Target"></bean>
<!--切面对象-->
<bean id="myAspect" class="com.lee.aop.MyAspect"></bean>
<!--配置织入:告诉spring框架 哪些方法(切点)需要进行哪些增强(前置、后置...)-->
<aop:config>
<!--声明切面-->
<aop:aspect ref="myAspect">
<!--切面:切点+通知-->
<aop:before method="before" pointcut="execution(public void com.lee.aop.Target.save())"></aop:before>
</aop:aspect>
</aop:config>
</beans>
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
@TestExecutionListeners( { DependencyInjectionTestExecutionListener.class })
public class AopTest {
@Autowired
private TargetInterface target;
@Test
public void test1(){
target.save();
}
}
切点表达式写法:
execution([修饰符] 返回值类型.包名.类名.方法名(参数))
例如:
execution(public void com.lee.aop.Target.save())
execution(void com.lee.aop.Target.*(..))
execution(* com.lee.aop.*.*(..))
execution(* com.lee.aop..*.*(..))
execution(* *..*.*(..))
通知的配置语法:
<aop:通知类型 method="切面类中方法名" pointcut="切点表达式></aop:通知类型>
名称 | 标签 | 说明 |
---|---|---|
前置通知 | 用于配置前置通知,指定增强的方法在切入点方法之前执行 | |
后置通知 | 用于配置后置通知,指定增强的方法在切入点方法之后执行 | |
环绕通知 | 用于配置环绕通知,指定增强的方法在切入点方法之前和之后都执行 | |
异常抛出通知 | 用于配置异常通知,指定增强的方法在出现异常时执行 | |
最终通知 | 用于配置最终通知,无论增强的方法是否有异常都会执行 |
该处使用的url网络请求的数据。
<?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
https://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="target" class="com.lee.aop.Target"></bean>
<!--切面对象-->
<bean id="myAspect" class="com.lee.aop.MyAspect"></bean>
<!--配置织入:告诉spring框架 哪些方法(切点)需要进行哪些增强(前置、后置...)-->
<aop:config>
<!--声明切面-->
<aop:aspect ref="myAspect">
<!--抽取切点表达式-->
<aop:pointcut id="myPointcut" expression="execution(public void com.lee.aop.Target.save())"/>
<!--切面:切点+通知-->
<aop:before method="before" pointcut-ref="myPointcut"></aop:before>
</aop:aspect>
</aop:config>
</beans>
public interface TargetInterface {
void save();
}
@Component("target")
public class Target implements TargetInterface {
@Override
public void save() {
System.out.println("save running.......");
}
}
@Component("myAspect")
@Aspect //标注当前MyAspect是一个切面类
public class MyAspect {
//配置前置增强
// @Before("execution(* com.lee.anno.*.*(..))")
public void before(){
System.out.println("前置增强......");
}
// @AfterReturning("execution(* com.lee.anno.*.*(..))")
public void afterReturning(){
System.out.println("后置增强......");
}
@Around("execution(* com.lee.anno.*.*(..))")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("环绕前置增强......");
Object proceed=pjp.proceed(); //切点方法
System.out.println("环绕后置增强......");
return proceed;
}
}
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="com.lee.anno"></context:component-scan>
<aop:aspectj-autoproxy/>
</beans>
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext-anno.xml")
@TestExecutionListeners( { DependencyInjectionTestExecutionListener.class })
public class AnnoTest {
@Autowired
private TargetInterface target;
@Test
public void test1() {
target.save();
}
}
运行查看测试结果
名称 | 标签 | 说明 |
---|---|---|
前置通知 | @Before | 用于配置前置通知,指定增强的方法在切入点方法之前执行 |
后置通知 | @AfterReturning | 用于配置后置通知,指定增强的方法在切入点方法之后执行 |
环绕通知 | @Around | 用于配置环绕通知,指定增强的方法在切入点方法之前和之后都执行 |
异常抛出通知 | @AfterThrowing | 用于配置异常通知,指定增强的方法在出现异常时执行 |
最终通知 | @After | 用于配置最终通知,无论增强的方法是否有异常都会执行 |
@Component("myAspect")
@Aspect //标注当前MyAspect是一个切面类
public class MyAspect {
//配置前置增强
// @Before("execution(* com.lee.anno.*.*(..))")
public void before(){
System.out.println("前置增强......");
}
// @AfterReturning("execution(* com.lee.anno.*.*(..))")
public void afterReturning(){
System.out.println("后置增强......");
}
// @Around("execution(* com.lee.anno.*.*(..))")
@Around("pointcut()") //切点表达式调用方式1,pointcut()
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("环绕前置增强......");
Object proceed=pjp.proceed(); //切点方法
System.out.println("环绕后置增强......");
return proceed;
}
@After("MyAspect.pointcut()") //切点表达式调用方式2,MyAspect.pointcut()
public void after(){
System.out.println("最终增强......");
}
@Pointcut("execution(* com.lee.anno.*.*(..))")
public void pointcut(){};
}