SpringAOP
实现代码复用,保持原有代码的结构(流程)不被改变,增强功能。
静态代理
在程序运行前就以经存在代理类的字节码文件,代理对像和被代理对象在运行前已经被确定。
优点:1、业务类只需关注业务类本身,保证了业务类的重用性。
? 2、把真实对象隐藏起来,保护真实对象。
缺点:1、代理对象的某个接口只服务于某一种类型的对象,也就是说每一个真实对象都得创建-个代理对象。
2、如果需要代理的方法很多,则要为每一种方法都进行代理处理。
3、如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
自己理解:新建一个类,被代理类作为一个成员变量,在创建代理类时,传入一个被代理类为代理类的成员变量赋值,在代理类中重写被代理类中需要代理的方法,在方法中用成员变量调用被代理类的方法,参入参数,获取返回值,在执行被代理类的方法的前后增加功能,作到不不改变业务逻辑的情况下,为系统增加功能。
例:
被代理类:
public class AdminServiceImpl implements AdminService{
@Overridepublic Admin login(String name, String pwd) {
if ("121".equals(name) && "212".equals(pwd)){
return new Admin("121","212");}return null;}@Overridepublic Admin add(String name, String pwd) {
return new Admin("121","212");}
}
代理类:
public class AdminProxy {
private AdminService service;public AdminProxy(AdminService service){
this.service=service;}public Admin login(String name, String pwd) {
Admin login = service.login(name, pwd);if (login!=null){
System.out.println("登录成功");}return login;}
}
测试:
@Test
public void oneTest(){
AdminService service = new AdminServiceImpl();AdminProxy proxy = new AdminProxy(service);Admin login = proxy.login("121", "212");System.out.println(login);
}
动态代理
在程序运行期间由jvm通过反射等机制动态的创建出代理对象的字节码,代理对象和真实对象的关系是在程序运行时才确定的。
jdk动态代理
Java动态代理机制生成的所有动态代理类的父类,它提供了一组静态方法来为一组接口动态地生成代理类及其对象
被代理类必须有一个实现de接口。
自己理解:运用Proxy.newProxyInstance(1,2,3)方法传入代理类的1ClassLoader对象,2interfaces数组和3一个实现InvocationHandler接口的自定义对象,在自定义对象中重写invoke方法,使用method.getName()过去当前调用被代理类对象的方法名,执行相应的增强操作。
例:
public class ProxyTest2 {
//被代理类AdminService service = new AdminServiceImpl();@Testpublic void Test2(){
//获取AdminService的ClassLoader对象ClassLoader classLoader = service.getClass().getClassLoader();//获取Class<?>[] interfacesClass<?>[] interfaces = new Class[]{
AdminService.class};//代理类AdminService adminService = (AdminService)Proxy.newProxyInstance(classLoader, interfaces, new MyAdminProxy2());Admin login = adminService.login("121", "212");System.out.println(login);adminService.add("21","21");}class MyAdminProxy2 implements InvocationHandler{
@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(method.getName());Object invoke = method.invoke(service, args);return invoke;}}
}
cglib动态代理
生成当前被代理类的子类
原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对fina修饰的类进行代理。
1、CGLIB可以生成目标类的子类,并重写父类非final修饰符的方法。
2、要求类不能是final的,要拦截的方法要是非final、 非static、 非private的。
3、动态代理的最小单位是类(所有类中的方法都会被处理);
自己理解:为一个Enhancer对象设置superClass即被代理类,和实现MethodInterceptor的自定义回调函数相关联,重写intercept方法,执行增强操作(获取当前方法,执行对应增强操作)。调用Enhancer的creat函数创建关联对象,强转为被代理类对象,即可调用被代理类对象的方法。在执行方法时,会执行相对应的增强操作。传入参数和返回值都不会改变。
例:
public class EnhancerTest {
@Testpublic void test1(){
//生成Enhancer对象Enhancer enhancer = new Enhancer();//指定Enhancer的字节码对象enhancer.setSuperclass(AdminServiceImpl.class);//设置回调函数enhancer.setCallback(new MyMethodInterceptor());AdminService service = (AdminService)enhancer.create();Admin login = service.login("121", "212");System.out.println(login);}class MyMethodInterceptor implements MethodInterceptor {
@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
Object invoke = methodProxy.invokeSuper(o, objects);if ("login".equals(method.getName())){
System.out.println("login");}return invoke;}}
}
AOP术语
连接点:一般指方法
切入点:需要增强功能的方法集合,确定通知的执行点
通知:真实用于增强方法的操作(功能)
切面:切入点+通知
通知
执行顺序
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A5KUz0ls-1601045200168)(C:\Users\84442\AppData\Roaming\Typora\typora-user-images\1595509349378.png)]
基于xml的aop
被代理类:
public class User {
public boolean login(String id){
if ("11".equals(id)){
System.out.println("用户存在");//return true;throw new RuntimeException("test");}return false;}
}
代理类:
public class demo1 {
//前置通知public void before(){
System.out.println("有人查询用户是否存在");}//后置通知public void after(){
System.out.println("after");}//完成通知public void afterRun(){
System.out.println("afterRun");}//异常通知public void afterThrowing(){
System.out.println("afterThrowing");}//环绕通知public void around(ProceedingJoinPoint point){
try {
point.proceed();} catch (Throwable throwable) {
throwable.printStackTrace();}}
}
xml配置:
<bean class="com.offcn.User" id="user"/><bean class="com.offcn.demo1" id="demo1"/><aop:config><aop:pointcut id="pointcut" expression="execution(* com.offcn.User.*(..))"/><aop:aspect ref="demo1"><!--前置通知--><aop:before method="before" pointcut-ref="pointcut"/><!--后置通知--><aop:after method="after" pointcut-ref="pointcut"/><!--完成通知--><aop:after-returning method="afterRun" pointcut-ref="pointcut"/><!--异常通知--><aop:after-throwing method="afterThrowing" pointcut-ref="pointcut"/></aop:aspect>
</aop:config>
基于注解的aop
编辑切面类(通知所在类):
@Aspect
@Component
public class demo2 {
//前置通知public void before(){
System.out.println("有人查询用户是否存在");}//后置通知public void after(){
System.out.println("after");}//完成通知public void afterRun(){
System.out.println("afterRun");}//异常通知public void afterThrowing(){
System.out.println("afterThrowing");}//环绕通知public void around(ProceedingJoinPoint point){
try {
point.proceed();} catch (Throwable throwable) {
throwable.printStackTrace();}}
}
必须注解@Aspect声明当前类是切面类
在spring中配置AOP代理自动配置
开启AOP代理自动配置
xml方式
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
注解方式
在配置类上添加
@EnableAspectJAutoProxy注解
全局与局部切入点
局部切入点
切入点方法:在切面类中定义方法,注解@Before、@After、@AfterReturning、@AfterThrowing、@Around
例:
无参局部
//前置通知
@Before("execution(* com.offcn.User.*(..))")
public void before(){
System.out.println("有人查询用户是否存在");
}
有参局部
//后置通知
@After("execution(* com.offcn.User.*(..)) && args(id)")
public void after(String id){
System.out.println(id);System.out.println("after");
}
全局切入点
在切面类中声明一个返回值为void的空方法,注解Pointcut(“aspect表达式 && 参数列表”)
例:
@Pointcut("execution(* com.offcn.spring.service.*.*(..)) && args(userName,userAge,birth)")public void beforePointCut(){
}
调用:
@Before("beforePointCut() && args(userName,userAge,birth)")
public void check(String userName, int userAge, Date birth) {
System.out.println("权限校验......" + userName + " " + userAge + " " + birth);
}
作用:使check方法成为beforePointCut()的aspect表达式匹配的内容的before通知,传入参数。
注:args中的参数名要和check的参数列表的参数名相同
? beforePointCut()方法可以在其他的饿切面类中调用
有参环绕通知:
@Around("execution(* com.offcn.User.*(..)) && args(id)")
public boolean around(ProceedingJoinPoint point,String id){
try {
boolean proceed = (boolean) point.proceed();System.out.println(proceed);return proceed;} catch (Throwable throwable) {
throwable.printStackTrace();}return false;
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qPi7gXza-1601045200193)(C:\Users\84442\AppData\Roaming\Typora\typora-user-images\1595572790771.png)]