Spring的AOP的基于AspectJ的XML的开发
- 摘要:AOP的概述
AOP:面向切面编程,是OOP(面向对象)的扩展和延伸,是用来解决OOP遇到问题。 - SpringAOP底层实现技术:
- JDK的动态代理实现(接口)
原理:利用反射的原理,给予真实业务对象生产代理对象,在真实业务执行的前后进行相关操作.
package javaApi.com;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/*
* 实现一个动态的代理设计模式
* 需要一个自定义proxy代理类来实现InvocationHandler
* 而这个自定义的代理类是
*/
class ALproxy implements InvocationHandler{//实现接口InvocationHandler
private Object target;//保留真实业务
/**
* 进行真实业务对象与代理业务对象的绑定处理
* @param target 真实的业务处理
* @return proxy代理业务生成的对象
*/
public Object bind(Object target) {
this.target=target;
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),this);
}
//切面通知
public boolean connect() {
System.out.println("开启消息通道");
return true;
}
//切面通知
public void close() {
System.out.println("关闭消息通道");
}
/*重写InvocationHandler里面的方法 外面去调用send();方法的时候,这个时候其实
代理对象进行调用invoke方法,需要经过InvocationHandler这个接口进行处理操作*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object returndata=null;
if(this.connect()) {
returndata=method.invoke(this.target, args);
this.close();
}
return returndata;
}
}
//发送消息的接口
interface Iinformation{
public abstract void send();
}
//实现消息接口
class information implements Iinformation {
public void send() {
System.out.println("information消息发送");
}
}
//测试
public class ital04 {
public static void main(String[] args) {
Iinformation infor=(Iinformation)new ALproxy().bind(new information());
infor.send();
}
}
- Cglib的动态代理实现(无接口)
原理:给予真实业务对象生产一个子类,继承真实业务,覆盖其中的方法实现增强,
/**
* 使用cglib动态代理
*
* @author yanbin
*
*/
class CglibProxy implements MethodInterceptor {
private Object target; //保留真实业务对象
/**
* 创建代理对象
* @param target 真实主体对象
* @return
*/
public Object getInstance(Object target) {
this.target = target;
Enhancer enhancer = new Enhancer(); //负责代理操作的程序类
enhancer.setSuperclass(this.target.getClass()); //假定一个父类
// 回调方法 设置代理类
enhancer.setCallback(this);
// 创建代理对象进行返回
return enhancer.create();
}
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object returndata=null;
if(this.connect()) {
returndata=methodProxy.invoke(this.target, args);
this.close();
}
return returndata;
}
}
//主体类
class Message{
public void send() {
System.out.println("information消息发送");
}
}
public class demo{
public static void main(String[] args) throws Exception{
Message message1=new Message();
Message message2=(Iinformation)new CglibProxy().getInstance(message);
//消息发送
message2.send();
}
}
- AOP的相关术语
连接点:可以被拦截的点。
切入点:真正被拦截的点。
通知:增强方法
引介:类的增强
目标:被增强的对象
织入:将增强应用到目标的过程。
代理:织入增强后产生的对象
切面:切入点和通知的组合 - AOP的入门开发
- 通知类型(名词解释百度)
前置通知
后置通知
环绕通知
异常抛出通知
最终通知
- Spring提供织入接口实现 (aopxmll开发)
<!-- aop的xml的基本配置 -->
<context:component-scan base-package="spring_aop_demo01.UserService"></context:component-scan>
<bean id="log" class="spring_aop_demo01.UserService.proxy.log"></bean>
<bean id="after" class="spring_aop_demo01.UserService.proxy.after"></bean>
<aop:config>
<!-- 切入点的配置 -->
<aop:pointcut expression="execution(* spring_aop_demo01.UserService.UserServiceImp.add(..))" id="pointcut1"/>
<aop:pointcut expression="execution(* spring_aop_demo01.UserService.UserServiceImp.seach(..))" id="pointcut2"/>
<!--切面的配置 -->
<aop:advisor advice-ref="log" pointcut-ref="pointcut1"/>
<aop:advisor advice-ref="after" pointcut-ref="pointcut2"/>
</aop:config>
log.java通知和after.java通知
//前置通知
public class log implements MethodBeforeAdvice{
/**
* 这个表示的就是前置通知
* method 表示的是被调用的方法的对象
* args 表示被调用方法的参数
* target 表示的是被调用方法的目标对象
*/
@Override
public void before(Method method, Object[] args, Object target)
throws Throwable {
System.out.println(target.getClass().getName()+"的"+method.getName()+"方法被执行了");
}
}
//后置通知
public class after implements AfterReturningAdvice {
/**
* returnValue 表示的是方法返回值
* method 被执行的方法
* agrs 表示执行方法参数
* target 被调用方法的目标对象
*/
@Override
public void afterReturning(Object returnValue, Method method, Object[] agrs,
Object target) throws Throwable {
System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行了");
//返回被调用方法的返回值
System.out.println("返回值"+returnValue);
}
}
切入点
@Service(value="UserService")
public class UserServiceImp implements UserService {
@Override
public void add() {
System.out.println("add====");
}
@Override
public String seach() {
System.out.println("seach====");
return "seach";
}
}
测试
@Test
public void fun(){
ApplicationContext app=new ClassPathXmlApplicationContext("applictionContext.xml");
UserService ser = (UserService)app.getBean("UserService");
ser.add();
ser.seach();
}
- 自定义通知(aopxml开发)
applictionContext.xml
<!-- aop的xml的基本配置 -->
<context:component-scan base-package="spring_aop_demo02.UserService"></context:component-scan>
<bean id="proxy" class="spring_aop_demo02.proxy.proxy"></bean>
<aop:config>
<!-- 切入点的配置 -->
<aop:pointcut expression="execution(* spring_aop_demo02.UserService.UserServiceImp.add(..))" id="pointcut1"/>
<aop:pointcut expression="execution(* spring_aop_demo02.UserService.UserServiceImp.seach(..))" id="pointcut2"/>
<aop:pointcut expression="execution(* spring_aop_demo02.UserService.UserServiceImp.update(..))" id="pointcut3"/>
<aop:pointcut expression="execution(* spring_aop_demo02.UserService.UserServiceImp.delete(..))" id="pointcut4"/>
<!--切面的配置 aspect 表示的是单个切面-->
<aop:aspect ref="proxy">
<!-- 为pointcut1切入点配置前置通知-->
<aop:before method="before" pointcut-ref="pointcut1"/>
<aop:after-returning method="afterreturn" pointcut-ref="pointcut2" returning="result"/>
<aop:around method="around" pointcut-ref="pointcut3"/>
<!-- 异常 -->
<aop:after-throwing method="throwable" pointcut-ref="pointcut4"/>
<!-- 最终通知after -->
<aop:after method="after" pointcut-ref="pointcut4"/>
</aop:aspect>
</aop:config>
通知
public class proxy {
public void before(){
System.out.println("前置通知=======");
}
// 这个地方的的result必须和前面的参数的返回值相同,才可以进行数据的返回
public void afterreturn(Object result){
System.out.println("后置通知=======" +"return"+result);
}
public void throwable(){
System.out.println("异常通知=======");
}
public void around(ProceedingJoinPoint join) throws Throwable{
System.out.println("环绕前通知=======");
Object proceed = join.proceed();
System.out.println("环绕后通知=======");
}
public void after(){
System.out.println("最终通知=======");
}
}
切入点
@Service(value="UserService")
public class UserServiceImp implements UserService{
@Value("123")
private String result;
@Override
public void add() {
System.out.println("add====");
}
@Override
public String seach() {
System.out.println("seach====");
return this.result;
}
@Override
public void delete() {
//造成一个异常的出的出现
int a=10/0;
System.out.println("delete====");
}
@Override
public void update() {
System.out.println("update====");
}
}
- AOP注解进行开发
applictionContext.xml
<!--使用注解进行aop的配置 -->
<!-- 在配置文件中开开启aop的注解开发 -->
<aop:aspectj-autoproxy/>
<bean id="user" class="spring_aop_demo01.Test.UserDao"></bean>
<bean id="proxy" class="spring_aop_demo01.Test.proxy"></bean>
切入点
public class UserDao implements User {
@Override
public void save() {
System.out.println("save");
}
}
通知
public class proxy {
@Before(value="execution( spring_aop_demo01.Test.UserDao.save(..))")
public void before(){
System.out.println("前置增强==========");
}
}
如果觉得这样将切入点配置早通知这个位置,如果不是很好区分,还可以进行如下的配置,将通知设置名称,然后进行切入点的配置操作:如下
public class proxy {
// aop的切面的配置操作
//前置通知
@Before(value="proxy.pointcut1()")
public void before(){
System.out.println("前置通知=======");
}
// 后置通知
// 这个地方的的result必须和前面的参数的返回值相同,才可以进行数据的返回
@AfterReturning(value="proxy.pointcut2()",returning="result")
public void afterreturn(Object result){
System.out.println("后置通知=======" +"return"+result);
}
//异常通知
@AfterThrowing(value="proxy.pointcut3()", throwing="e")
public void throwable(Throwable e){
System.out.println("异常通知======="+e.getMessage());
}
//环绕通知
@Around(value="proxy.pointcut4()")
public Object around(ProceedingJoinPoint join) throws Throwable{
System.out.println("环绕前通知=======");
Object proceed = join.proceed();
System.out.println("环绕后通知=======");
return proceed;
}
//最终通知
@After(value="proxy.pointcut3()")
public void after(){
System.out.println("最终通知=======");
}
// 进行切入点的配置操作
@Pointcut(value="execution( spring_aop_demo02.UserService.UserServiceImp.add(..))")
private void pointcut1(){}
@Pointcut(value="execution(* spring_aop_demo02.UserService.UserServiceImp.seach(..))")
private void pointcut2(){}
@Pointcut(value="execution(* spring_aop_demo02.UserService.UserServiceImp.delete(..))")
private void pointcut3(){}
@Pointcut(value="execution(* spring_aop_demo02.UserService.UserServiceImp.update(..))")
private void pointcut4(){}
测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applictionContext2.xml")
public class Test {
@Resource(name="user")
private User user;
@org.junit.Test
public void fun(){
user.save();
}
}
Spring的JDBC的模板使用
Spring对持久层也提供了解决方案:ORM模块和JDBC的模板。
applictionContext_c3p0.xml
<!-- 在这里我们可以进行配置文件的抽取操作 -->
<!-- 第一种引入的操作 将配置文件设置到location中来 -->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:jdbc.properties"></property>
</bean>
<!-- 第二种引入的操作context加载类目录下面的资源文件直接引入配置文件的中的指定位置-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 将配置管理交给spring来进行管理操作 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driverClass}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="user" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!-- 创建jdbc模板 -->
<bean id="jdbctemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
DAO层
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applictionContext_c3p0.xml")
public class Test_c3p0_demo {
@Resource(name="jdbctemplate")
private JdbcTemplate jdbcTemplate;
@Test
public void fun() {
String sql="insert account values(null,?,?)";
jdbcTemplate.update(sql, "jree",400d);
}
//@Test//修改数据
public void fun2() {
String sql="update account set money=? where id=?";
jdbcTemplate.update(sql,1000d,1);
}
//@Test//删除数据
public void fun3() {
String sql="delete from account where id=?";
jdbcTemplate.update(sql,3);
}
@Test //进行数据的查询操作(查询一个属性值)
public void fun4() {
String sql="select name from account where id=?";
String queryForObject = jdbcTemplate.queryForObject(sql, String.class,1);
System.out.println(queryForObject);
}
@Test //统计查询
public void fun5() {
String sql="select count(*) from account";
Long count=jdbcTemplate.queryForObject(sql, Long.class);
System.out.println(count);
}
@Test// 查询数据返回一个对象 使用jdbc模板的时候需要自己进行手动的存储数据到对象中
public void fun6() {
String sql="select *from account where id=?";
Account acc=jdbcTemplate.queryForObject(sql, new MyRowMapper(),1);
System.out.println(acc);
}
@Test// 查询数据返回一个一个集合数据
public void fun7() {
String sql="select *from account";
List<Account> query = jdbcTemplate.query(sql, new MyRowMapper());
for (Account account : query) {
System.out.println(account.toString());
}
}
}
/*
* 封装一个类的操作 这个地方就是让我们实现这个RowMapper
* 这个接口里面的方法 将结果集封装到一个对象进行数据的返回
* 这个地方如果是需要获取所有的数据的一个集合的话,可以实现一个res.next
* 角标查询的操作,将所有的数据都封装到一个集合中来
*/
class MyRowMapper implements RowMapper<Account>{
@Override
public Account mapRow(ResultSet res, int row) throws SQLException {
Account acc=new Account();
acc.setId(res.getInt("id"));
acc.setName(res.getString("name"));
acc.setMoney(res.getDouble("money"));
return acc;
}
}
Account .java 封装的3个属性,不用关心数据库的数据,主要是了解spring提供的JDBC模块的操作
private int id ;
private String name;
private double money;
Spring事务的三种配置方式
- 说到事务,在这里解释一下事务(逻辑上的一组操作,组成这组操作的各个单元,要么全都成功,要么全都失败)
- 事务的特性4点:
原子性:事务不可分割
一致性:事务执行前后数据完整性保持一致
隔离性:一个事务的执行不应该受到其他事务的干扰
持久性:一旦事务结束,数据就持久化到数据库 - 安全性问题
—读问题
脏读 :一个事务读到另一个事务未提交的数据
不可重复读:一个事务读到另一个事务已经提交的update的数据,导致一个事务中多次查询结果不一致 虚读、幻读:一个事务读到另一个事务已经提交的insert的数据,导致一个事务中多次查询结果不一致。
—写问题
丢失更新 - 事务的隔离级别
Read uncommitted :未提交读,任何读问题解决不了。
Read committed :已提交读,解决脏读,但是不可重复读和虚读有可能发生。(mysql支持)
Repeatable read :重复读,解决脏读和不可重复读,但是虚读有可能发生。(orcal)
Serializable :解决所有读问题。
- 下面介绍spring中事务的操作
- 平台事务管理器(PlatformTransactionManager):接口,是Spring用于管理事务的真正的对象。
DataSourceTransactionManager:底层使用JDBC管理事务
HibernateTransactionManager:底层使用Hibernate管理事务 - 定义事务信息(TransactionDefinition)
用于定义事务的相关的信息,隔离级别、超时信息、传播行为、是否只读 - 事务状态(TransactionStatus)
用于记录在事务管理过程中,事务的状态的对象
关系:Spring进行事务管理的时候,首先平台事务管理器根据事务定义信息进行事务的管理,在事务管理过程中,产生各种状态,将这些状态的信息记录到事务状态的对象中。 - 传播行为
-
Spring中提供了七种事务的传播行为:
保证多个操作在同一个事务中
PROPAGATION_REQUIRED :默认值,如果A中有事务,使用A中的事务,如果A没有,创建一个新的事务,将操作包含进来
PROPAGATION_SUPPORTS:支持事务,如果A中有事务,使用A中的事务。如果A没有事务,不使用事务。
PROPAGATION_MANDATORY :如果A中有事务,使用A中的事务。如果A没有事务,抛出异常。 -
保证多个操作不在同一个事务中
PROPAGATION_REQUIRES_NEW :如果A中有事务,将A的事务挂起(暂停),创建新事务,只包含自身操作。如果A中没有事务,创建一个新事务,包含自身操作。
PROPAGATION_NOT_SUPPORTED :如果A中有事务,将A的事务挂起。不使用事务管理。
PROPAGATION_NEVER :如果A中有事务,报异常。 -
嵌套式事务
PROPAGATION_NESTED :嵌套事务,如果A中有事务,按照A的事务执行,执行完成后,设置一个保存点,执行B中的操作,如果没有异常,执行通过,如果有异常,可以选择回滚到最初始位置,也可以回滚到保存点。
第一种编程式事务
applictionContext.xml
<!--配置平台事务管理器============================= -->
<bean id= "transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--配置事务管理的模板========将平台事务管理其注入到配置管理器模板中-->
<bean id="TransactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager"/>
</bean>
编写事务操作类
/**实现一个转账的操作(编写式事务)
* from 转账人
* to 收账人
* money 转账的金额
*/
@Override
public void transfer(String from, String to, double money) {
//函数式的编写事务配置方法 这个其实就是aop的技术
this.transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus arg0) {
//from减少钱
dao.outMoney(from,money);
//to加钱
//抛出一个异常
//int a=10/0;
dao.inMoney(to,money);
}
});
}
第二种声明式事务xml实现
applictionContext.xml
<!--配置平台事务管理器============================= -->
<bean id= "transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置事务 的增强操作 =======-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--事务管理的规则========= -->
<tx:attributes>
<!-- 对name方法进行事务的添加 propagation表示是传递方式 isolation表示是隔离的状态 -->
<tx:method name="*" propagation="REQUIRED" isolation="DEFAULT"/>
</tx:attributes>
</tx:advice>
<!-- 配置aop切入点 -->
<!--进行aop的配置 切入点配置 ===========-->
<aop:config>
<aop:pointcut expression="execution(* spring_jdbc_tx_demo02.UserService.Imp.UserServiceImp.*(..))" id="pointcut1"/>
<!-- pointcut-ref事务的指定切入点配置 advice-ref="txAdvice" 配置切面-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut1"/>
</aop:config>
编写事务操作
/**实现一个转账的操作(声明事务)
* from 转账人
* to 收账人
* money 转账的金额
*/
@Override
public void transfer(String from, String to, double money) {
//from减少钱
userdao.outMoney(from,money);
//to加钱
//抛出一个异常
//int a=10/0;
userdao.inMoney(to,money);
}
第三种声明式事务注解开发
applictionContext.xml
<!--配置平台事务管理器============================= -->
<bean id= "transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 开启事务注解 声明-->
<tx:annotation-driven transaction-manager="transactionManager"/>
编写事务操作类
//注解开发
@Transactional(isolation =Isolation.DEFAULT,propagation = Propagation.REQUIRED)
public class UserServiceImp implements UserService {
private UserDao dao;
public void setDao(UserDao dao) {
this.dao = dao;
}
/**实现一个转账的操作(声明式事务)
* from 转账人
* to 收账人
* money 转账的金额
*/
@Override
public void transfer(String from, String to, double money) {
//from减少钱
dao.outMoney(from,money);
//to加钱
//抛出一个异常
int a=10/0;
dao.inMoney(to,money);
}
}
spring事务的配置就是通过的aop的思想进行实现的.
参考说明:黑马程序员资料