1、概述:面向切面编程。是通过预编译方式(aspectj)或者运行期动态代理(Spring)实现程序功能的统一维护的技术。
2、作用:在不修改源码的情况下,进行功能增强,通过动态代理进行实现。
3、优势:减少重复代码,提高开发效率,方便维护。
4、AOP的底层实现:Spring的AOP,底层是通过动态代理实现的。在运行期间,通过代理技术动态生成代理对象,代理对象方法执行时进行功能的增强介入,再去调用目标方法,从而完成功能增强。
二、spring的AOP1、AOP相关概念:
-
目标对象(Target):要代理的/要增强的目标对象。
-
代理对象(Proxy):目标对象被AOP织入增强后,就得到一个代理对象
-
连接点(JoinPoint):能够被拦截到的点,在Spring里指的是方法
目标类里,所有能够进行增强的方法,都是连接点
-
切入点(PointCut):要对哪些连接点进行拦截的定义
已经增强的连接点,叫切入点
-
通知/增强(Advice):拦截到连接点之后要做的事情
对目标对象的方法,进行功能增强的代码
-
切面(Aspect):是切入点和通知的结合
-
织入(Weaving):把增强/通知 应用到 目标对象来创建代理对象的过程。Spring采用动态代理技术织入,而AspectJ采用编译期织入和装载期织入
AOP开发前要明确的事项
我们要做的事情:
-
编写核心业务代码(Target目标类的目标方法)
-
编写通知类,通知类中有通知方法(Advice增强功能方法)
-
在配置文件中,配置织入关系,即将哪些通知与哪些切入点 结合,形成切面
Spring的AOP做的事情:
-
生成动态代理的过程(把通知织入到切入点的过程),是由Spring来实现的
-
Spring会监控切入点方法的执行,一旦发现切入点方法执行,使用代理机制动态创建目标对象的代理对象,根据通知类别,在代理对象的对应位置,将通知对应的功能织入,完成完整的代码逻辑运行。
1、步骤分析
-
创建maven项目,导入AOP需要的依赖坐标
-
创建目标类,创建通知类
-
使用注解
@Component
标注两个类,配置成为bean对象 -
在增强类身上,需要打上注解 @Aspect ,让其成为增强类|对象
-
在增强类的方法上,打注解@Before | @AfterReturing | ... 表示想要增强谁。
-
-
在配置文件中,开启组件扫描和AOP的自动代理(自动装配)
-
测试
2、代码实现
1、创建maven项目,导入坐标,注意:需要增加AOP的实现包:`aspectjweaver
`
junit junit4.12 test org.springframework spring-context5.1.12.RELEASE org.springframework spring-aop5.1.12.RELEASE org.aspectj aspectjweaver1.9.6
2、创建目标类和通知类/增强类
public interface UserService { void add(); void update(); }
@Service public class UserServiceImpl implements UserService { @Override public void add() { System.out.println("调用了UserServiceImpl的add方法"); } @Override public void update() { System.out.println("调用了UserServiceImpl的aupdate方法"); } }
@Aspect //声明成为增强类 @Component //标注成为bean对象 public class Myadvice { @Before("execution(* com.itheima..*..*(..))") public void sprint() { System.out.println("打印了日志-----"); }
3、编写核心配置类
@Configuration //标注成为核心配置类 @ComponentScan("com.itheima") //开启组件扫描 @EnableAspectJAutoProxy //允许AOP的自动配置 public class AppConfig { }
4、编写测试方法
@RunWith(SpringJUnit4ClassRunner.class) //用在测试类上,用于声明不再使用Junit,而是使用Spring提供的运行环境 //Spring提供了单元测试的运行环境:SpringJunit4ClassRunner @ContextConfiguration(classes = AppConfig.class)//用于指定核心配置类 public class TestUserServiceImpl { @Autowired private UserService us; @Test public void test(){ us.add(); } }注解解释:
1、通知的类型:
注意:
-
注解方式配置的通知,执行顺序是:
前置->最终->后置/异常
-
如果想要指定执行的顺序,就使用环绕通知 , 因为环绕增强是由我们手动控制的
2、切点表达式
语法:execution ( [ 权限修饰符 ] 返回值类型 包名 . 类名 . 方法名 ( 参数列表 ) )
修饰符:可省略
返回值类型:进行类型指定,如String等。*表示任意修饰符,
包名:可以写【.】,代表当前目录的下一级文件。写【..】,代表当前包下的任意级类或包
类名:可指定类名,【*】代表任意字符
方法名:可以指定方法名,【*】代表任意字符
参数:可以指定类型,如string,integer。【..】代表任意类型,任意个数的参数
示例:
execution(public void com.itheima.dao.impl.UserDao.save())
execution(* com.itheima.dao..*.*(..))
三、spring事务管理1、概念:
所谓事务管理,即:按照给定的事务规则,来执行提交或回滚 *** 作。其中:
-
"给定的事务规则":用
TransactionDefinition
表示 -
"按照..来执行提交或回滚 *** 作":用
PlatformTransactionManager
来完成 -
TransactionStatus
用于表示一个运行着的事务的状态
需求:
1、完成一个银行转账功能
2、定义dao和service层,完成扣钱和加钱的功能
3、注册事务管理,并在类上添加事务注解
代码:
1、编写dao层和service层
public interface AccountDao { @Update("update account set money = money - #{money} where name = #{from}") void kouqian(@Param("from") String from, @Param("money") int money); @Update("update account set money = money + #{money} where name = #{to}") void jiaqian(@Param("to") String to, @Param("money") int money); }
public interface AccountService { public void zhuanzhang(String from,String to,int money); }
@Transactional//类里面的方法都有事务 @Service public class AccountServiceImpl implements AccountService { @Autowired private AccountDao dao; public void zhuanzhang(String from, String to, int money) { dao.kouqian(from, money); int a = 1 / 0;//在扣钱后编写一个错误,运行后查看数据库扣钱和加强是否同步 dao.jiaqian(to, money); } }
2、编写核心配置类
@Configuration //声明此类是核心配置类 @ComponentScan("com.itheima") //开启组件扫描 @PropertySource("db.properties") //获取properties文件 @EnableTransactionManagement //开启事务的注解驱动 public class AppConfig { @Value("${db.driverClassName}") private String driverClassName; @Value("${db.url}") private String url; @Value("${db.username}") private String username; @Value("${db.password}") private String password; @Bean public DataSource ds() { DruidDataSource ds = new DruidDataSource(); ds.setDriverClassName(driverClassName); ds.setUrl(url); ds.setUsername(username); ds.setPassword(password); return ds; } @Bean public SqlSessionFactoryBean factory(DataSource ds) { SqlSessionFactoryBean factory = new SqlSessionFactoryBean(); factory.setDataSource(ds); return factory; } @Bean public static MapperScannerConfigurer msc() { MapperScannerConfigurer configurer = new MapperScannerConfigurer(); configurer.setBasePackage("com.itheima.dao"); return configurer; } @Bean public DataSourceTransactionManager tm(DataSource ds) { DataSourceTransactionManager tm = new DataSourceTransactionManager(); tm.setDataSource(ds); return tm; } }
3、编写测试方法
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = AppConfig.class) public class TestAccountServiceImpl { @Autowired private AccountService as; @Test public void test(){ as.zhuanzhang("zs","ls",100); } }
4、进行测试,在扣钱方法后添加错误,运行后查看数据库金额的增减是否同步。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)