UserDao接口以及实现类
public interface UserDao {
void insertUser();
void updateUser();
void deleteUser(int id);
List<String> selectUser();
}
public class UserDaoImpl implements UserDao {
@Override
public void insertUser() {
//在方法种主要业务逻辑代码开始之前,加入日志记录
System.out.println("日志:用户新增...");
System.out.println("通过JDBC实现用户新增...");
}
@Override
public void updateUser() {
System.out.println("日志:用户修改...");
System.out.println("通过JDBC实现用户修改...");
}
@Override
public void deleteUser(int id) {
System.out.println("日志:用户删除...");
System.out.println("通过JDBC实现用户删除...");
}
@Override
public List<String> selectUser() {
System.out.println("日志:用户查询...");
System.out.println("通过JDBC实现用户查询...");
return null;
}
}
测试
public class ProxyTest {
@Test
public void test(){
UserDao userDao = new UserDaoImpl();
userDao.insertUser();
userDao.updateUser();
userDao.deleteUser(10);
List<String> list = userDao.selectUser();
System.out.println(list);
}
}
使用代理模式解决上述问题
代理设计模式的原理:使用一个代理将对象包装起来,然后用该代理对象取代原始对象,任何对原始对象的调用都要通过代理,代理对象决定是否以及何时将方法调用转到原始对象上
每个业务接口都需要有一个对应的代理类,并实现业务接口
public class UserDaoProxy implements UserDao {
private UserDao userDao;
public UserDaoProxy(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void insertUser() {
System.out.println("日志记录:用户新增...");
userDao.insertUser();
}
@Override
public void updateUser() {
System.out.println("日志记录:用户修改...");
userDao.updateUser();
}
@Override
public void deleteUser(int id) {
System.out.println("日志记录:用户删除..." + id);
userDao.deleteUser(id);
}
@Override
public List<String> selectUser() {
System.out.println("日志记录:用户查询...");
return userDao.selectUser();
}
}
测试
public class ProxyTest {
@Test
public void testStaticProxy(){
UserDao userDao = new UserDaoProxy(new UserDaoImpl());
userDao.insertUser();
userDao.updateUser();
userDao.deleteUser(10);
List<String> list = userDao.selectUser();
System.out.println(list);
}
}
JDK动态代理:采用Java反射机制中的动态代理,实现InvocationHandler接口
public class LoggerProxy implements InvocationHandler {
/**
* 执行目标(最终要执行业务逻辑类)方法
* @param proxy 代理类对象
* @param method 目标方法对象
* @param args 目标方法的参数值列表
* @return 目标方法的返回值
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//日志记录的代码
System.out.println("代理日志:目标方法--前置" + method.getName() + "....");
//执行目标方法(业务逻辑方法)
Object result_val = null;
try{
result_val = method.invoke(targetClass.newInstance(), args);
System.out.println("代理日志:目标方法--返回" + method.getName() + "....");
}catch (Exception e){
System.out.println("代理日志:目标方法--异常" + method.getName() + "....");
}
System.out.println("代理日志:目标方法--后置" + method.getName() + "....");
return result_val;
}
/**
* 目标对象的Class类型
*/
private Class targetClass;
/**
* 获取代理对象
*
* 注意:JDK中的动态代理,要求业务类必须是接口+实现类的形式
*/
public Object getProxy(Class targetClass){
this.targetClass = targetClass;
return Proxy.newProxyInstance(targetClass.getClassLoader(),targetClass.getInterfaces(),this);
}
}
测试
public class ProxyTest {
@Test
public void testDynamicProxy(){
UserDao userDao = (UserDao) new LoggerProxy().getProxy(UserDaoImpl.class);
System.out.println(userDao);
userDao.insertUser();
userDao.updateUser();
userDao.deleteUser(10);
List<String> list = userDao.selectUser();
System.out.println(list);
System.out.println("---------------------------");
PersonDao personDao = (PersonDao) new LoggerProxy().getProxy(PersonDaoImpl.class);
personDao.insertPerson();
}
}
Cglib动态代理,实现cglib包下的MethodInterceptor接口
public class CglibProxy implements MethodInterceptor {
/**
*
* @param obj CGLib动态生成的代理类对象
* @param method 目标方法对象
* @param args 目标方法的参数值列表
* @param proxy 代理类对方法的代理对象
* @return 目标方法的返回值
*/
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("Cglib动态代理...");
//执行目标方法(业务逻辑方法)
Object result_val = null;
try{
result_val = method.invoke(targetClass.newInstance(), args);
}catch (Exception e){
}
return result_val;
}
/**
* 目标对象的Class类型
*/
private Class targetClass;
/**
* 获取代理对象
*
* 注意:JDK中的动态代理,要求业务类必须是接口+实现类的形式
*/
public Object getProxy(Class targetClass){
//为目标对象target赋值
this.targetClass = targetClass;
Enhancer enhancer = new Enhancer();
//设置父类,因为Cglib是针对指定的类生成一个子类,所以需要指定父类
enhancer.setSuperclass(targetClass);
//设置回调
enhancer.setCallback(this);
//创建并返回代理对象
Object result = enhancer.create();
return result;
}
}
测试
public class ProxyTest {
@Test
public void testCglibProxy(){
UserDao userDao = (UserDao) new CglibProxy().getProxy(UserDaoImpl.class);
System.out.println(userDao);
userDao.insertUser();
userDao.updateUser();
userDao.deleteUser(10);
List<String> list = userDao.selectUser();
System.out.println(list);
}
}
maven依赖配置
<dependencies>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-coreartifactId>
<version>4.3.18.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-beansartifactId>
<version>4.3.18.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>4.3.18.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-expressionartifactId>
<version>4.3.18.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-aopartifactId>
<version>4.3.18.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-aspectsartifactId>
<version>4.3.18.RELEASEversion>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
<scope>testscope>
dependency>
dependencies>
在spring的配置文件中加入aop命名空间以及aop标签规范
xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
定义一个普通Java类,在其中添加功能方法
/**
* 日志切面类:日志记录功能
*/
public class LogAspect {
/**
* 前置通知:在目标方法执行之前实现的功能
*/
public void beforeMethod(){
System.out.println("AOP日志记录:前置通知......");
}
}
切面完整代码
/**
* 日志切面类:日志记录功能
*/
public class LogAspect {
/**
* 前置通知:在目标方法执行之前实现的功能
* 参数:JoinPoint连接点对象,可获取当前要执行的目标方法的信息
*/
public void beforeMethod(JoinPoint joinPoint){
//通过连接点对象获取方法的名称
String methodName = joinPoint.getSignature().getName();
//方法的参数值列表
Object[] args = joinPoint.getArgs();
System.out.println("AOP日志记录:前置通知......" + methodName + Arrays.toString(args));
}
/**
* 后置通知:在目标方法执行之后实现的功能
*/
public void afterMethod(JoinPoint joinPoint){
//通过连接点对象获取方法的名称
String methodName = joinPoint.getSignature().getName();
//方法的参数值列表
Object[] args = joinPoint.getArgs();
System.out.println("AOP日志记录:后置通知......" + methodName + Arrays.toString(args));
}
/**
* 返回通知:在目标方法有返回值之后实现的功能
* 通过方法的参数获取目标方法的返回值数据
*/
public void afterReturnMethod(JoinPoint joinPoint, Object resultValue){
//通过连接点对象获取方法的名称
String methodName = joinPoint.getSignature().getName();
//方法的参数值列表
Object[] args = joinPoint.getArgs();
System.out.println("AOP日志记录:返回通知......" + methodName + Arrays.toString(args));
System.out.println("方法的返回值为:" + resultValue);
}
/**
* 异常通知:在目标方法抛出异常之后实现的功能
* 通过方法参数获取目标方法抛出的异常对象
*/
public void afterThrowMethod(JoinPoint joinPoint, Exception ex){
//通过连接点对象获取方法的名称
String methodName = joinPoint.getSignature().getName();
//方法的参数值列表
Object[] args = joinPoint.getArgs();
System.out.println("AOP日志记录:异常通知......" + methodName + Arrays.toString(args));
System.out.println("方法抛出的异常对象:" + ex);
}
/**
* 环绕通知:综合以上所有的通知
*/
public void aroundMethod(ProceedingJoinPoint proceedingJoinPoint){
System.out.println("AOP日志记录:环绕通知之前置......");
try {
//手动执行目标方法
//proceed()就是在执行目标方法,其返回值为目标方法的返回值
Object resultValue = proceedingJoinPoint.proceed();
System.out.println("AOP日志记录:环绕通知之返回......");
} catch (Throwable throwable) {
System.out.println("AOP日志记录:环绕通知之异常......");
throwable.printStackTrace();
}
System.out.println("AOP日志记录:环绕通知之后置......");
}
}
<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"
xmlns:p="http://www.springframework.org/schema/p"
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/util
http://www.springframework.org/schema/util/spring-util.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">
<bean id="userDao" class="com.newcapec.dao.UserDaoImpl"/>
<bean id="personDao" class="com.newcapec.dao.PersonDaoImpl"/>
<bean id="log" class="com.newcapec.aspect.LogAspect"/>
<aop:config>
<aop:pointcut id="exp1" expression="execution(* com.newcapec.dao.*Impl.*(..))"/>
<aop:pointcut id="exp2" expression="execution(public void com.newcapec.dao.UserDaoImpl.insertUser())"/>
<aop:pointcut id="exp3" expression="execution(* com.newcapec.dao.UserDaoImpl.findUser(..))"/>
<aop:pointcut id="exp4" expression="execution(* com.newcapec.dao.PersonDaoImpl.insertPerson(..))"/>
<aop:aspect ref="log">
<aop:before method="beforeMethod" pointcut-ref="exp1"/>
<aop:after method="afterMethod" pointcut-ref="exp2"/>
<aop:after-returning method="afterReturnMethod" pointcut-ref="exp3" returning="resultValue"/>
<aop:after-throwing method="afterThrowMethod" pointcut-ref="exp4" throwing="ex"/>
aop:aspect>
aop:config>
beans>
public class AOPTest {
@Test
public void test(){
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao = ac.getBean("userDao", UserDao.class);
userDao.insertUser();
System.out.println("------------------");
userDao.updateUser();
System.out.println("------------------");
userDao.deleteUser(1);
System.out.println("------------------");
userDao.selectUser();
System.out.println("------------------");
PersonDao personDao = ac.getBean("personDao", PersonDao.class);
personDao.insertPerson();
}
}
注意:在目标方法出现异常后,返回通知不再执行。但是在目标方法没有出现异常时,异常通知不会执行
标签中配置order属性,属性值为数字。数字越小优先级越高,该切面功能越先被执行
/**
* 另一个切面类
*/
public class OtherAspect {
public void beforeM(){
System.out.println("OtherAspect的beforeM方法.....");
}
}
<bean id="other" class="com.newcapec.aspect.OtherAspect"/>
<aop:config>
<aop:aspect ref="log" order="2">
aop:aspect>
<aop:aspect ref="other" order="1">
<aop:before method="beforeM" pointcut-ref="exp1"/>
aop:aspect>
aop:config>