本文是于2020-4-25重构,将文章中关于Spring的理念介绍移至:
你一直在用Spring,但你可能真的不懂它
本文为IOC的实操部分,介绍Spring的一些基本操作
你最后已经掌握spring的一些基本操作,在
[Spring]关于IOC控制反转,你应该掌握这些中,我详细介绍了如何上手spring及其IOc操作
AOC基于动态代理模式
如果以下概念看不懂没关系,看下面实操代码就整明白了
Jionpoint连接点):要拦截的目标方法
Pointcut(切点):拦截目标方法后所执行的方法
Advice(通知):在连接点前后或异常情况来做的事情,分为前置通知,后置通知,返回通知,异常通知,环绕通知
Aspect(切面):切点和通知组成
Target(目标对象):代理的目标对象,如上图
Weave(织入):切面应用到目标对象并且导致proxy(代理)对象创建的过程叫织入
你需要从Maven仓库找到以下红色框内的包,版本不要求,导入你的项目中【通过坐标】
Spring配置文件内容(AplicationContext.xml)
<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-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd">
<aop:aspectj-autoproxy/>
beans>
package com.aspect;
import com.model.User;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
//切面类加上注解@Aspect
@Aspect
public class PermAspect {
/*
* 第一个*:拦截方法的返回值,*代表任意
* 第一个..子包下
* 第二个*:所有的类
* 第三个*:所有的方法
* 第二个..所有的参数
* */
@Pointcut("execution(* com.service..*.*(..))")
public void anyMethod(){
}
/*
*Before前置通知
*anyMethod():指定前置通知的切点
*args(参数)
* */
@Before("anyMethod()&&args(user)")
/*或者写成以下形式
* @Before(value = "anyMethod()&&args(param)",argNames = "param")
* */
public void preAdvice(User user){
System.out.println(user);
System.out.println("执行前置通知");
}
}
在配置文件中配置切面bean与被切的目标bean
<bean id="userDao" class="com.daoimpl.UserDaoImpl">bean>
<bean id="userDaoService" class="com.serviceimpl.UserDaoServiceImpl">
<property name="userDao" ref="userDao">property>
bean>
<bean id="permApect" class="com.aspect.PermAspect">bean>
连接点:
package com.serviceimpl;
import com.dao.UserDao;
import com.model.User;
import com.service.UserDaoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
public class UserDaoServiceImpl implements UserDaoService {
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
private UserDao userDao ;
@Override
public void save() {
userDao.save();
}
/*连接点->要拦截的方法*/
@Override
public void saveUser(User user) {
userDao.saveUser(user);
}
}
测试
public static void main(String[] args) {
User user = new User("三金",15) ;
//创建spring容器
ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
//从容器中获得bean
UserDaoService service = (UserDaoService) context.getBean("userDaoService");
//调用方法
service.saveUser(user);
}
结果
前置通知:
如上所示
后置通知:
如上所示,改成**@After**注解,在业务方法执行之后来执行,其余使用方法和前置拦截一样
返回通知:
在返回通知里面可以对业务方法的返回值做最后的统一加工
如:
/**
* pointcut:指定返回通知的切点
* returning:指定返回值和返回通知的方法的参数名称要一致
*
* @param user
*/
@AfterReturning(pointcut = "anyMethod()",returning = "user")
public void returnAdvice(User user){
user.setName("四金");
System.out.println("执行返回通知");
System.out.println(user);
}
最常用的一种通知,该通知类型可以取代以上所有的通知
package com.aspect;
import com.model.User;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
//切面类加上注解@Aspect
@Aspect
public class PermAspect {
@Pointcut("execution(* com.service..*.*(..))")
public void anyMethod(){
}
@Around("anyMethod()") //环绕通知的注解
public Object aroundAdvice(ProceedingJoinPoint jp){
//获得拦截的业务方法的参数,返回值为Object数组
Object[] args = jp.getArgs();
System.out.println("环绕通知-前"+"-------传递的参数:"+args[0]);
//相当于返回通知的操作
Object obj = null;
User user = null;
try {
//proceed执行业务方法,也可以使用无参数,返回值就是业务方法的返回值
obj = jp.proceed(args);
user = (User) obj;
} catch (Throwable e) {
//例外通知处理逻辑可在catch中处理
e.printStackTrace();
}
//后置通知
user.setName("五金");
System.out.println("环绕通知-后"+"-------返回值:"+obj);
return obj;
}
}
<aop:config>
<aop:pointcut expression="execution(* com.rl.spring.service..*.*(..))" id="mycut"/>
<aop:aspect ref="permApect">
<aop:before method="preAdvice" pointcut-ref="mycut"/>
<aop:after method="afterAdvice" pointcut-ref="mycut"/>
<aop:after-returning method="returnAdvice" pointcut-ref="mycut" returning="returnval"/>
<aop:after-throwing method="exceptionAdvice" pointcut-ref="mycut" throwing="ex"/>
<aop:around method="aroundAdvice" pointcut-ref="mycut"/>
aop:aspect>
aop:config>
JoinPoint类可以方便我们操作参数信息,一般少用
public class PermAspect {
public void preAdvice(JoinPoint jp){
Object[] args = jp.getArgs();
if(args.length > 0)
System.out.println("执行前置通知-----------业务参数:"+args[0]);
}
public void afterAdvice(JoinPoint jp){
Object[] args = jp.getArgs();
if(args.length > 0)
System.out.println("执行后置通知-----------业务参数:"+args[0]);
}
public void returnAdvice(JoinPoint jp, Object returnval){
if(returnval instanceof User){
User user = (User) returnval;
user.setUsername("六金");
}
System.out.println("返回通知----------返回的值"+returnval);
}
public void exceptionAdvice(JoinPoint jp, Exception ex){
System.out.println("例外通知------------");
ex.printStackTrace();
}
public Object aroundAdvice(ProceedingJoinPoint jp){
Object[] objs = jp.getArgs();
if(objs.length>0)
System.out.println("执行环绕通知前-----------业务参数:"+objs[0]);
Object obj = null;
try {
obj = jp.proceed();
System.out.println("执行环绕通知后-----------业务参数:"+objs[0]);
} catch (Throwable e) {
e.printStackTrace();
}
return obj;
}
}