AOP:
事务代理:
代理模式
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aoeGdHrc-1630481036077)(C:\Users\ZYZ\AppData\Roaming\Typora\typora-user-images\)]
主要包含的三个角色:
-
主体类角色:
可以是接口或者抽象类,需要让目标对象或者代理对象去实现或者继承,目的是为了让目标对象和代理对象具有相同的方法;
-
目标类角色:
他的对象就是目标类对象,核心业务逻辑的具体执行者;
-
代理类角色:
他的对象就是代理对象,内部还有对真是目标对象的引用,负责对目标对象方法的调用,并且在调用前后进行预处理
-
静态代理
-
动态代理
- 创建目标类对象
- 创建目标类的类加载
- 获得目标类所实现的接口
- 创建
InvocationHandler
接口的实现类对象
AOP在Spring中的作用
提供声明式事务,允许用户自定义切面
特点:
- 减少代码的重复
- 提高开发效率
- 提高可维护性
实现
Spring框架中,会自动根据目标对象是否实现了接口,来选择使用JDK动态代理的方式来实现,还是CGLIB技术来实现
术语
-
切面/切面类(aspect)
:- 将来要被织入到方法执行 前/后/异常 的时候去执行的代码片段
-
连接点(joinpoint)
:- Spring的连接点时目标对象里面需要被代理的方法,默认情况时目标对象中所有非
final
修饰的方法; - 如果不是在SpringAOP中,join Point可能还会是属性
- Spring的连接点时目标对象里面需要被代理的方法,默认情况时目标对象中所有非
-
切入点(pointCut)
:- 一组连接点的集合,就是一个切入点,因为连接点在Spring中就相当于方法,所以一个切入点也就是一组方法的集合
-
通知/拦截器(advice)
:- 控制切面/切面类 将来要在目标对象中方法的什么位置执行,例如方法的前面或者方法的后面,或者是在抛出异常的时候
-
织入(wave)
:- 将切面类织入到指定方法中去执行的动作
-
目标对象(target)
:- 需要被代理的对象,一般是代理目标对象的一个或者多个指定的方法
-
代理对象(proxy)
:- 代理目标对象,在完成核心功能的前提下,添加额外的代码去执行
SpringAOP中,通过Advice定义横切逻辑,Spring中支持的5重类型的Advice:
通知类型 | 连接点 | 实现接口 |
---|---|---|
前置通知 | 方法前 |
|
后置通知 | 方法后 |
|
环绕通知 | 方法前后 |
|
异常抛出通知 | 方法抛出异常 |
|
引介通知 | 类中增加新的方法属性 |
|
即AOP在不改变原有代码的情况下去增加新的功能
案例
首先引入架包
<!-- AOP切面相关的包-->
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId></groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
1.自定义类来实现AOP
接口类:
public interface ITeacherService {
void add();
void delete();
void update();
}
实现接口
//目标类
@Service
public class ITeacherServiceImpl implements ITeacherService {
@Override
public void add() {
System.out.println("add ....");
}
@Override
public void delete() {
System.out.println("delete ...");
}
@Override
public void update() {
System.out.println("update ...");
}
}
定义切面
@Aspect
@Component
//切面
public class MyAccept {
//注解配置
@Pointcut("execution(* .*.*(..))")
public void pointcut1(){
}
//注解配置
@Pointcut("execution(* .*.*(..))")
public void pointcut2(){
}
//通知
@Before("pointcut1()")
public void beforeAdvide(){
System.out.println("before ...");
}
@After("pointcut2()")
public void afterAdvice(){
System.out.println("after ...");
}
public void afterReturn(){
}
}
配置类
@Configuration
@ComponentScan(basePackages = "")
@EnableAspectJAutoProxy
public class AcceptConfig {
}
@EnableAspectJAutoProxy
加了该注解才会是切面类的配置生效
测试
@RunWith(SpringJUnit4ClassRunner.class)
//@ContextConfiguration("classpath:")
@ContextConfiguration(classes = AcceptConfig.class)
public class acceptTest {
@Autowired
private ITeacherService iTeacherservice;
@Test
public void test_1(){
iTeacherservice.add();
System.out.println(iTeacherservice.getClass());
}
}
以上是注解的方式,也可通过配置文件的方法
配置文件
<beans xmlns="/schema/beans"
xmlns:aop="/schema/aop"
xmlns:xsi="http:///2001/XMLSchema-instance"
xsi:schemaLocation="/schema/beans
/schema/beans/
/schema/aop
/schema/aop/">
<!-- 目标对象-->
<bean name="teacherService" class=""/>
<!-- 切面(通知)-->
<bean name="myAccept" class=""/>
<!-- 代理-->
<aop:config>
<aop:pointcut id="myaop1" expression="execution(* .*.*(..))"/>
<aop:pointcut id="myaop2" expression="execution(* .*.*(..))"/>
<aop:aspect ref="myAccept">
<aop:before method="beforeAdvide" pointcut-ref="myaop1"/>
<aop:after method="afterAdvice" pointcut-ref="myaop2"/>
</aop:aspect>
</aop:config>
</beans>
通过Spring API实现
目标接口和目标实现了与上一致
然后去写增强类,这里编写两个,一个前置增强,一个后置增强
public class Log implements MethodBeforeAdvice {
/**
*
* @param method 要执行的目标对象的方法
* @param objects 被调用方法的参数
* @param o 目标对象
* @throws Throwable
*/
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println(o.getClass().getName()+"的"+method.getName()+"方法被执行了");
}
}
public class AfterLog implements AfterReturningAdvice {
/**
*
* @param returnValue 返回值
* @param method 被调用的方法
* @param args 被调用的方法的参数列表
* @param target 被调用的目标对象
* @throws Throwable
*/
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("执行了"+target.getClass().getName()
+"的"+method.getName()+"方法,"+"返回值:"+returnValue);
}
}
<!--注册bena-->
<bean id="teacherService" class=""/>
<bean id="log" class=""/>
<bean id="afterlog" class=""/>
<!--aop的配置-->
<aop:config>
<!--切入点 expression:表达式匹配要执行的方法 *:任意返回值类型 service包下以及所有子包的任意类的任意方法-->
<aop:pointcut id="pointcut" expression="execution(* ..*.*(..))"/>
<!--环绕执行:advice-ref 执行方法 pointcut-ref:切入点-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterlog" pointcut-ref="pointcut"/>
</aop:config>
</beans>
测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:")
//@ContextConfiguration(classes = )
public class acceptTest {
@Autowired
private ITeacherService iTeacherservice;
@Test
public void test_2(){
iTeacherservice.add();
System.out.println(iTeacherservice.getClass());
}
}
Spring的Aop就是将公共的业务 (日志 , 安全等) 和领域业务结合起来 , 当执行领域业务时 , 将会把公共业务加进来 . 实现公共业务的重复利用 . 领域业务更纯粹 , 程序猿专注领域业务 , 其本质还是动态代理
Spring-day3-作业
1.请按照自己的理解描述AOP是什么
AOP
:意思为面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护技术。利用AOP可以对业务逻辑层的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高开发效率。
2.请使用xml配置,完成环绕、前置、返回后、异常等通知
(1)切面类:
public class RoleAspect {
public RoleVerifier roleVerifier;
public void before(Role role) {
System.out.println("before..." + role.getRoleName() + "引入了参数");
}
public void after() {
System.out.println("after...");
}
public void afterReturning() {
System.out.println("afterReturning");
}
public void afterThrowing() {
System.out.println("afterThrowing");
}
//环绕通知
public void around(ProceedingJoinPoint jp) {
System.out.println("around-before");
try {
jp.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println("around-after");
}
}
(2)IRoleService以及RoleServiceImpl:
public interface IRoleService {
public void print(Role role)throws Exception;
}
@Service("roleService")
public class RoleServiceImpl implements IRoleService{
@Override
public void print(Role role) throws Exception {
System.out.println("print方法执行");
}
}
@Data
@Component
public class Role implements Serializable {
@Value("1")
private Integer id;
@Value("admin")
private String username;
@Value("123456")
private String password;
}
Unbound pointcut parameter
由于前置方法中传入了参数,所以再配置前置执行方法时要传入参数,则不能用
pointcut-ref
,而要使用pointcut
xml
<beans xmlns="/schema/beans"
xmlns:aop="/schema/aop"
xmlns:xsi="http:///2001/XMLSchema-instance"
xmlns:context="/schema/context"
xsi:schemaLocation="/schema/beans
/schema/beans/
/schema/aop
/schema/aop/ /schema/context /schema/context/">
<bean name="RoleAspect" class=""/>
<bean name="role" class=""/>
<aop:config>
<aop:pointcut id="p1" expression="execution(* .*.*(..))"/>
<aop:aspect ref="RoleAspect">
<aop:before method="before" pointcut="execution(* .*.*(..)) and args(role)"/>
<aop:after method="after" pointcut-ref="p1"/>
<aop:after-returning method="afterReturning" pointcut-ref="p1"/>
<aop:after-throwing method="afterThrowing" pointcut-ref="p1"/>
<aop:around method="around" pointcut-ref="p1"/>
</aop:aspect>
</aop:config>
<context:component-scan base-package=""/>
</beans>
test
@RunWith(SpringJUnit4ClassRunner.class)
//@ContextConfiguration(classes = )
@ContextConfiguration(locations = "classpath:")
public class RoleTest {
@Autowired
private Role role;
@Autowired
private RoleService roleService;
@Test
public void test() throws Exception {
// (role);
roleService.print(role);
}
}
3.谈谈 Spring 事务管理的三个重要概念。
- 事务的隔离级别
- 事务的传播行为
- 事务的回滚原则
- 事务的只读属性
- 事务超时
4.事务的隔离级别是
-
ISOLATION_DEFAULT
:事务默认的隔离级别- MYSQL:可重复读
- ORACLE:读已提交
-
ISOLATION_UNCOMMITTED
:读未提交 -
ISOLATION_COMMITTED
:读已提交 -
ISOLATION_REPEATABLE_READ
:可重复读 -
ISOLATION_SERIALIZABLE
:串行化