1.JDK动态代理
(1) 什么是动态代理?
代理类在程序运行时创建的代理方式。代理类不是java类定义好的,而是在运行时根据我们在java代码中的“指示”动态生成的。
2. JDK动态代理和CGLIB动态代理
(1) JDK动态代理和CGLIB动态代理的应用场景?
如果目标对象实现了接口,我们采用JDK动态代理
如果目标对象没有实现了接口,我们采用CGLIB动态代理
(2) 使用JDK动态代理和CGLIB动态代理的实现步骤?
JDK动态代理
//定义目标对象
SomeService target = new SomeServiceImpl();
//定义目标对象的代理对象
SomeService proxy = (SomeService)Proxy.newProxyInstance(target.getClass().getClassLoader()//目标类的类加载器
, target.getClass().getInterfaces()//目标类实现的所有的接口
, new InvocationHandler() {//调用处理器
//proxy 代理对象
//method 目标方法
//args 目标方法参数
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String result = (String) method.invoke(target, args);
return result.toUpperCase();
}
});
String result = proxy.doSome();
System.out.println(result);
CGLIB动态代理
代理工厂类:
//1、cglib动态代理工厂
public class CglibProxyFactory implements MethodInterceptor{
private SomeServiceImpl target;
//3、添加构造方法
public CglibProxyFactory(SomeServiceImpl target) {
super();
this.target = target;
}
public CglibProxyFactory() {
super();
}
//2、创建cglib代理对象的方法
public SomeServiceImpl proxyCreator(){
//创建增强器对象
Enhancer enhancer = new Enhancer();
//指定父类(目标类)
enhancer.setSuperclass(SomeServiceImpl.class);
//指定回调接口对象
enhancer.setCallback(this);
//创建cglib代理对象
return (SomeServiceImpl) enhancer.create();
}
//4、重写intercrpt方法,对目标对象进行增强
@Override
public String intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
String result = (String) method.invoke(target, args);
return result.toUpperCase();
}
}
测试类
public class SomeTest {
public static void main(String[] args) {
//定义目标对象
SomeServiceImpl target = new SomeServiceImpl();
//定义目标对象的代理对象
SomeServiceImpl proxy = new CglibProxyFactory(target).proxyCreator();
System.out.println(proxy.doSome());
}
}
4. 基于Schema-based方式之前置通知
(1) 如何搭建AOP编程环境?
(2) 如何创建前置通知?
配置文件
<?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="someServiceImpl" class="com.bjsxt.service.impl.SomeServiceImpl"></bean>
<!-- 注册切面,前置通知 -->
<bean id="myMethodBeforeAdvice" class="com.bjsxt.aspects.MyMethodBeforeAdvice"></bean>
<!-- 注册切面,后置通知 -->
<bean id="myAfterReturningAdvice" class="com.bjsxt.aspects.MyAfterReturningAdvice"></bean>
<!-- 注册代理 -->
<bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 指定目标对象 -->
<property name="target" ref="someServiceImpl"></property>
<!-- 指定目标类的实现的所有接口 -->
<property name="interfaces" value="com.bjsxt.service.SomeService"></property>
<!-- 指定切面 -->
<property name="interceptorNames" value="myMethodBeforeAdvice"></property>
</bean>
</beans>
前置通知类:
package com.bjsxt.aspects;
import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;
public class MyMethodBeforeAdvice implements MethodBeforeAdvice{
/**
* method 目标方法
* args 目标方法参数列表
* target目标对象
*
*/
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("前置通知的before()方法执行了");
}
}
5. 基于Schema-based方式之后置通知
(1) 基于Schema-based方式之后置通知
package com.bjsxt.aspects;
import java.lang.reflect.Method;
import org.springframework.aop.AfterReturningAdvice;
public class MyAfterReturningAdvice implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("后置通知方法MyAfterReturningAdvice.afterReturning()执行"+returnValue);
if(returnValue!=null){
System.out.println("后置通知方法执行"+((String)returnValue).toUpperCase());
}
}
}
(2) 在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="someServiceImpl" class="com.bjsxt.service.impl.SomeServiceImpl"></bean>
<!-- 注册切面,前置通知 -->
<bean id="myMethodBeforeAdvice" class="com.bjsxt.aspects.MyMethodBeforeAdvice"></bean>
<!-- 注册切面,后置通知 -->
<bean id="myAfterReturningAdvice" class="com.bjsxt.aspects.MyAfterReturningAdvice"></bean>
<!-- 注册代理 -->
<bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 指定目标对象 -->
<property name="target" ref="someServiceImpl"></property>
<!-- 指定目标类的实现的所有接口 -->
<property name="interfaces" value="com.bjsxt.service.SomeService"></property>
<!-- 指定切面 -->
<property name="interceptorNames" value="myAfterReturningAdvice"></property>
</bean>
</beans>
6. 基于Schema-based方式之环绕通知
(1) 如何创建环绕通知?
package com.bjsxt.aspects;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
//切面,环绕通知
public class MyMethodInterceptor implements MethodInterceptor {
/**
*invacation 方法调用器
*/
@Override
public Object invoke(MethodInvocation invacation) throws Throwable {
System.out.println("环绕通知:目标方法执行之前");
Object result = invacation.proceed();
if(result!=null){
result = ((String)result).toUpperCase();
}
System.out.println("环绕通知:目标方法执行之后");
return result;
}
}
(2) 环绕通知是否能调用目标方法,如果可以,通过哪个对象调用?
通过invacation 方法调用器来调用目标方法
7. 基于Schema-based方式之异常通知
(1) 如何创建异常通知?
package com.bjsxt.aspects;
import org.springframework.aop.ThrowsAdvice;
public class MyThrowsAdvice implements ThrowsAdvice {
public void afterThrowing(Exception ex){
System.out.println("异常通知执行");
}
}
(2) 异常通知能否获得异常信息,需要如何配置?
在目标方法中添加异常信息
比如 / by zero
package com.bjsxt.service.impl;
import com.bjsxt.service.SomeService;
public class SomeServiceImpl implements SomeService {
@Override
public void doSome() {
System.out.println("SomeServiceImpl.doSome()方法执行"+1/0);
}
@Override
public String doOther() {
System.out.println("SomeServiceImpl.doOther()方法执行");
return "love";
}
}
添加配置文件:
<?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="someServiceImpl" class="com.bjsxt.service.impl.SomeServiceImpl"></bean>
<!-- 注册切面,前置通知 -->
<bean id="myMethodBeforeAdvice" class="com.bjsxt.aspects.MyMethodBeforeAdvice"></bean>
<!-- 注册切面,后置通知 -->
<bean id="myAfterReturningAdvice" class="com.bjsxt.aspects.MyAfterReturningAdvice"></bean>
<!-- 注册切面,环绕通知 -->
<bean id="myMethodInterceptor" class="com.bjsxt.aspects.MyMethodInterceptor"></bean>
<!-- 注册切面,异常通知 -->
<bean id="myThrowsAdvice" class="com.bjsxt.aspects.MyThrowsAdvice"></bean>
<!-- 注册代理 -->
<bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 指定目标对象 -->
<property name="target" ref="someServiceImpl"></property>
<!-- 指定目标类的实现的所有接口 -->
<property name="interfaces" value="com.bjsxt.service.SomeService"></property>
<!-- 指定切面 -->
<property name="interceptorNames" value="myThrowsAdvice"></property>
</bean>
</beans>
8.基于AspectJ方式
(1)描述Spring、AspectJ和AOP三者之间的关系?
(2)AspectJ支持的通知常见有哪些?
前置通知、后置通知、环绕通知、异常通知和最终通知
9基于AspectJ方式之环境搭建
(1)导入jar包
(2)引入AOP约束
<?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>
10基于AspectJ方式之注解方式
1、目标接口
package com.bjsxt.service;
public interface SomeService {
void doSome();
String doOther();
}
2、目标接口的实现类
package com.bjsxt.service.impl;
import com.bjsxt.service.SomeService;
public class SomeServiceImpl implements SomeService {
@Override
public void doSome() {
//System.out.println("SomeServiceImpl.doSome()方法执行"+1/0);
System.out.println("SomeServiceImpl.doSome()方法执行");
}
@Override
public String doOther() {
System.out.println("SomeServiceImpl.doOther()方法执行");
return "love";
}
}
3、定义切面类
package com.bjsxt.aspects;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
//切面
@Aspect//表名当前类是一个切面
public class MyAspect {
//该注解表明当前方法是前置通知方法
@Before("execution(* *..service.*.doSome(..))")
public void before(){
System.out.println("前置通知方法执行");
}
//该注解表明当前方法是后置通知方法
@AfterReturning(value="execution(* *..service.*.doOther(..))",returning="result")
public void afterReturning(Object result){
System.out.println("后置通知方法执行,返回结果是:"+result);
}
//该注解表明当前方法是环绕通知方法
@Around("execution(* *..service.*.doOther(..))")
public Object around(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("环绕通知:目标方法执行之前");
String result = (String) pjp.proceed();
if(result!=null){
result = result.toUpperCase();
}
System.out.println("环绕通知:目标方法执行之后");
return result;
}
//该注解表明当前方法是异常通知方法
@AfterThrowing(value="execution(* *..service.*.doSome(..))",throwing="ex")
public void throwing(Exception ex){
System.out.println("异常通知方法执行!异常通知信息为:"+ex);
}
//该注解表明当前方法是最终通知方法
@After("execution(* *..service.*.doSome(..))")
public void after(){
System.out.println("最终通知方法执行");
}
}
4、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"
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">
<!-- 注册目标类-->
<bean id="someServiceImpl" class="com.bjsxt.service.impl.SomeServiceImpl"></bean>
<!-- 注册切面 -->
<bean id="myAspect" class="com.bjsxt.aspects.MyAspect"></bean>
<!-- 注册自动代理 -->
<aop:aspectj-autoproxy/>
</beans>
5、测试类
package com.bjsxt.test;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.bjsxt.service.SomeService;
public class SomeTest {
@Test
public void someTest01(){
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
SomeService someService = (SomeService) ac.getBean("someServiceImpl",SomeService.class);
someService.doSome();
System.out.println(someService.doOther());
}
}
注意:测试时五种通知分开测试,先注释掉其他四种,其中包括配置文件与切面类的注释。
11基于AspectJ方式之XML方式
1、2、5与基于AspectJ方式之注解方式相同省略
3、定义切面类
package com.bjsxt.aspects;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
public class MyAspect {
//无参前置方法
public void before(){
System.out.println("前置通知方法执行");
}
//带参前置方法
public void before(JoinPoint jp){
System.out.println("前置通知方法执行!jp="+jp);
}
public void afterReturning(Object result){
System.out.println("后置通知方法执行,返回结果是:"+result);
}
public Object around(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("环绕通知:目标方法执行之前");
String result = (String) pjp.proceed();
if(result!=null){
result = result.toUpperCase();
}
System.out.println("环绕通知:目标方法执行之后");
return result;
}
public void throwing(Exception ex){
System.out.println("异常通知方法执行!异常通知信息为:"+ex);
}
public void after(){
System.out.println("最终通知方法执行");
}
}
4、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"
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">
<!-- 注册目标类-->
<bean id="someServiceImpl" class="com.bjsxt.service.impl.SomeServiceImpl"></bean>
<!-- 注册切面 -->
<bean id="myAspect" class="com.bjsxt.aspects.MyAspect"></bean>
<!-- AOP配置 -->
<aop:config>
<!-- 定义切入点 -->
<aop:pointcut expression="execution(* *..service.*.doSome(..))" id="doSomePC"/>
<aop:pointcut expression="execution(* *..service.*.doOther(..))" id="doOtherPC"/>
<!-- 切面 -->
<aop:aspect ref="myAspect">
<!-- <aop:before method="before" pointcut-ref="doSomePC"/> -->
<!-- <aop:before method="before(org.aspectj.lang.JoinPoint)" pointcut-ref="doSomePC"/> -->
<aop:after-returning method="afterReturning(java.lang.Object)" pointcut-ref="doOtherPC" returning="result"/>
<!-- <aop:around method="around" pointcut-ref="doOtherPC"/> -->
<!-- <aop:after-throwing method="throwing(java.lang.Exception)" pointcut-ref="doSomePC" throwing="ex"/> -->
<!-- <aop:after method="after" pointcut-ref="doSomePC"/> -->
</aop:aspect>
</aop:config>
</beans>