java02-爱代码爱编程
今日内容
1. 代理设计模式
静态代理模式 前提 代理角色和真实角色继承同一个类 理解 静态代理模式相当于在代理角色中,调用真实角色 -真实角色只完成业务操作 -代理角色完成增强业务-边角料 -最后调用代理角色即可
package com.szr.service;
/**
* 用户业务接口
*/
public interface UserService {
/**
* 获取用户信息
* @return 返回用户信息
*/
String getUser();
}
package com.szr.service.impl;
import com.szr.service.UserService;
/**
* 用户业务接口实现类--真实角色
*/
public class UserServiceImpl implements UserService {
/**
* 获取用户信息
* @return 返回用户信息
*/
@Override
public String getUser() {
return "获取到用户信息了--钟离";
}
}
package com.szr.staticProxy;
import com.szr.service.UserService;
/**
* 用户静态代理角色-代理角色帮助完成增强业务
*/
public class UserStaticProxy implements UserService {
//声明业务接口,后面用真实角色实现,代理角色调用
private UserService userService ;
//用真实角色来实现-这里替换的是真实角色的对象,方便调用
public UserStaticProxy(UserService userService){
this.userService = userService ;
}
@Override
public String getUser() {
//完成增强业务--比如用户查阅权限校验
System.out.println("已完成对用户的权限校验");
//实现真实业务
String user = userService.getUser();
//完成增强业务--比如记录操作
System.out.println("已完成对查阅的记录");
return user;
}
}
/**
* 对静态代理的测试
*/
@Test
public void StaticProxyTest(){
//创建真实对象的实例
UserService userService = new UserServiceImpl() ;
//调用代理角色--传入真实角色对象,完成替换
UserService userService1 = new UserStaticProxy(userService);
//调用代理角色方法--里面的逻辑是,代理角色再调用真实角色
String user = userService1.getUser();
System.out.println(user);
/*
已完成对用户的权限校验
已完成对查阅的记录
获取到用户信息了--钟离
*/
}
jdk动态代理模式 前提 必须有一个接口,因为jdk动态代理是基于接口进行的 理解 代理模式都是先访问代理角色,再由代理角色访问真实角色,有真实角色反馈结果,再由代理角色反馈结果 jdk动态代理是拦截器和反射共同进行的 -必须实现InvocationHandler接口--获取真实角色的方法 -使用Proxy.newProxyInstance( 代理类要实现的接口的类加载器 ClassLoader loader, 代理类要实现的接口列表 Class<?>[] interfaces, 代理处理程序,代理角色具体内容 InvocationHandler handler )--获取代理角色对象,在调用方法
package com.szr.jdkProxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* jdk动态代理必须实现InvocationHandler接口--相当于代理角色
*/
public class UserJdkProxy implements InvocationHandler {
//声明任意对象,最后替换成需要完成的真实角色的对象
private Object object ;
public UserJdkProxy(Object object){
this.object = object ;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//代理角色完成代理业务
System.out.println("已经完成用户权限校验");
//通过对真实角色对象的利用,通过反射的方式获取真实角色的方法
Object obj = method.invoke(object, args);
//代理角色完成业务增强
System.out.println("已经完成日志记录");
return obj;
}
}
/**
* jdk动态代理测试
*/
@Test
public void JdkProxyTest(){
//创建真实角色对象
UserService userService = new UserServiceImpl();
//创建jdkProxy对象,返回的是目标角色-真实角色的方法
UserJdkProxy handler = new UserJdkProxy(userService);
//获取代理对象
UserService userService1 = (UserService) Proxy.newProxyInstance(
userService.getClass().getClassLoader(),
userService.getClass().getInterfaces(),
handler
);
//调用方法
String user = userService1.getUser();
System.out.println(user);
/*
已经完成用户权限校验
已经完成日志记录
获取到用户信息了--钟离
*/
}
cglib动态代理模式 基于子类实现代理模式 实现接口 cglib提供的InvocationHandler 需要导入cglib.jar包 步骤 1)创建增强类,通过增强类对真实角色产生代理 2)使用增强类对指定的类型的Class产生代理-设置目标类型 3)设置回调-当前类对象的地址值引用 4)通过增强类创建代理实例
package com.szr.cglibProxy;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.InvocationHandler;
import java.lang.reflect.Method;
/**
* cglib完成动态代理
*/
public class cglibProxy implements InvocationHandler {
//还是传入真实角色对象替换对象
private Object object ;
public cglibProxy(Object object){
this.object = object ;
}
//自定义一个方法完成对代理角色的创建
public Object getObject(){
//创建增强类
Enhancer enhancer = new Enhancer();
//使用增强类对指定类型的Class产生代理
enhancer.setSuperclass(object.getClass());
//设置回调-this当前类地址引用
enhancer.setCallback(this);
//使用增强类创建代理实例
Object obj = enhancer.create();
return obj ;
}
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
//完成代理角色增强业务
System.out.println("已经校验过权限");
Object invoke = method.invoke(object, objects);
System.out.println("已经记录到日志");
return invoke;
}
}
@Test
public void CglibProxyTest(){
//创建真实对象
UserService userService = new UserServiceImpl();
//创建cglib的handler对象
cglibProxy cglib = new cglibProxy(userService);
UserService userService1 = (UserService) cglib.getObject();
String user = userService1.getUser();
System.out.println(user);
/*
已经校验过权限
已经记录到日志
获取到用户信息了--钟离
*/
}
2. SpringAOP技术
SpringAOP Spring核心技术之一 理解 就是Spring方式的代理模式 思路 使用xml配置方式/注解配置方式 再Spring配置文件中管理对应类/开启全局注解扫描 测试类中使用即可
xml文件配置方式 分开配置方式 自定义类继承对应的接口 -业务前执行接口-MethodBeforeAdvice -业务后执行接口-AfterReturningAdvice -业务报错执行接口-ThrowsAdvice 合在一起配置方式 -业务环绕执行接口-MethodInterceptor-相当于将上面的执行步骤合在一起 配置Spring通知 切点以及通知类型 aop:config aop:advisor 属性advice-ref:指定通知类型 上面id标识一致 属性pointcut:切点表达式:SpringAOP方式找到业务方法进行方法增强 切点表达式语法: 第一个*必须写:固定语法格式 第二个*和第一个*中间有一个空格的 第三个*: 包名的第一个包 第四个*: 具体业务接口所在的包名service 第五个*: 接口实现类型的包名impl 第六个*: 指定的类名 第七个*: 指定的方法名 (..):不明确参数使用(..) 组装切面 aop:aspect id="指定唯一标识" ref="关联切面类" 配置通知同时指定切点表达式,这样很麻烦! spring提供的切点定义 aop:pointcut id="唯一标识" pointcut:切点表达式 注解配置方式 开启SpringAOP注解方式通过 切面给业务功能进行增强,开启注解让Spring扫描AOP相关的注解 <aop:aspectj-autoproxy/> 定义切面类,使用注解的方式 分开配置 将所有的业务接口分开织入切面类,单独成每一个方法,单独使用注解 合在一起配置 定义一个环绕通知方法-将所有业务增强都用进去,使用一个环绕注解
分开配置方式 自定义类继承对应的接口 -业务前执行接口-MethodBeforeAdvice -业务后执行接口-AfterReturningAdvice -业务报错执行接口-ThrowsAdvice
package com.szr.springAOC;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
//执行业务前通知
public class BeforeHandler implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("已经进行权限校验!");
}
}
package com.szr.springAOC;
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
//执行业务后通知
public class AfterHandler implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
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: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
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--将用户实现类,真实角色管理起来-->
<bean id="userService" class="com.szr.service.impl.UserServiceImpl"/>
<!--管理执行前通知-->
<bean id="beforeHandler" class="com.szr.springAOC.BeforeHandler"></bean>
<!--管理执行后通知-->
<bean id="afterHandler" class="com.szr.springAOC.AfterHandler"></bean>
<!--配置Spring通知 切点以及通知类型-->
<aop:config>
<!--配置执行前通知 关联执行前通知类 后面是切点-就是要执行的内容,在执行这个内容的时候切入-->
<aop:advisor advice-ref="beforeHandler" pointcut="execution(* *.*.service.impl.*.*(..))"></aop:advisor>
<!--配置执行后通知 关联执行后通知类-->
<aop:advisor advice-ref="afterHandler" pointcut="execution(* *.*.service.impl.*.*(..))"></aop:advisor>
</aop:config>
</beans>
/**
* SpringAOC普通版测试
*/
@Test
public void SpringAOCCommonTest(){
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("spring-config.xml");
UserService userService = (UserService) context.getBean("userService");
String user = userService.getUser();
System.out.println(user);
/*
已经进行权限校验!
已经记录在日志!
获取到用户信息了--钟离
*/
}
合在一起配置方式 -业务环绕执行接口-MethodInterceptor-相当于将上面的执行步骤合在一起
package com.szr.springAOC;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import java.lang.reflect.Method;
//普通环绕通知,将分散的通知整合
public class MyRound implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
//准备执行前通知
System.out.println("已经校验过了");
//产生代理实例,反射调用业务方法
Object obj = invocation.proceed();
//准备执行后通知
System.out.println("已经记录在日志");
return obj;
}
}
<?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
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--将用户实现类,真实角色管理起来-->
<bean id="userService" class="com.szr.service.impl.UserServiceImpl"/>
<!--管理环绕通知类-->
<bean id="myRound" class="com.szr.springAOC.MyRound"></bean>
<!--配置Spring通知 切点以及通知类型-->
<aop:config>
<!--配置环绕通知-->
<aop:advisor advice-ref="myRound" pointcut="execution(* *.*.service.impl.*.*(..))"></aop:advisor>
</aop:config>
</beans>
/**
* SpringAOC普通方式整合版
*/
@Test
public void SpringAOCRound(){
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("spring-config.xml");
UserService userService = (UserService) context.getBean("userService");
String user = userService.getUser();
System.out.println(user);
/*
已经校验过了
已经记录在日志
获取到用户信息了--钟离
*/
}
定义切面类,使用注解的方式 分开配置 将所有的业务接口分开织入切面类,单独成每一个方法,单独使用注解
package com.szr.springAOC;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
//切面类,注解使用
@Component
@Aspect//标记为切面类
public class MyAspect {
//定义一个方法,指定切点表达式
@Pointcut("execution(* *.*.service.impl.*.*(..))")
public void PointCut(){}
//定义一个方法,执行前通知,直接将切点表达式的方法名放进去
@Before("PointCut()")
public void BeforeHandler(){
System.out.println("真的已经校验过权限了");
}
//定义一个方法,执行后通知,直接将切点表达式的方法名放进去
@AfterReturning("PointCut()")
public void AfterHandler(){
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: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
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--将用户实现类,真实角色管理起来-->
<bean id="userService" class="com.szr.service.impl.UserServiceImpl"/>
<!--管理执行前通知-->
<bean id="beforeHandler" class="com.szr.springAOC.BeforeHandler"></bean>
<!--管理执行后通知-->
<bean id="afterHandler" class="com.szr.springAOC.AfterHandler"></bean>
<!--管理环绕通知类-->
<bean id="myRound" class="com.szr.springAOC.MyRound"></bean>
<!--管理环绕通知类注解方式-->
<bean id="myAspect" class="com.szr.springAOC.MyAspect"></bean>
<!--开启SpringAOP注解方式通过 切面给业务功能进行增强,开启注解让Spring扫描AOP相关的注解-->
<aop:aspectj-autoproxy/>
</beans>
/**
* SpringAOC注解使用方式
*/
@Test
public void SpringAOCAspect(){
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("spring-config.xml");
UserService userService = (UserService) context.getBean("userService");
String user = userService.getUser();
System.out.println(user);
/*
真的已经校验过权限了
真的已经记录在日志了
获取到用户信息了--钟离
*/
}