1 使用注解进行配置
首先肯定是导入jar包,除了第一天的四个以外,还需要导入aop的jar包
1.1 导入约束
可以从docs\spring-framework-reference\html文件夹下找到xsd-configuration.html文件进行导入,是在40.2.8那个位置。
<?xml version="1.0" encoding="UTF-8"?>
<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" xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
1.2 开启使用注解代理配置文件
<!-- 指定扫面com.dlj.bean包下的所有注解 -->
<context:component-scan base-package="com.dlj.entity"></context:component-scan>
1.3在类中使用注解完成配置
@Component( value="user")
// <bean name="user" class="...">
public class User {
private String name;
private Integer age;
private Car car;
}
现在的注解除了@Component,还有@Service,@Controller,@Repository,分别是用于service层、web层、还有dao层
1.4 常用的注解
@Scope注解指定对象的作用范围,是单例还是多例
@Scope(value="prototype")
1.4.1 值类型注入
如果要给类中的某一个字段赋值,通过注解有两种方法,一种是把注解放到字段上面,还有一种是放到set方法上面,如下:
@Value(value = "tr")
private String name;
这种事通过反射进行赋值,破坏了封装性
或者是下面:
@Value(value = "tr")
public void setName(String name) {
this.name = name;
}
这种通过set方法进行赋值,是比较推荐使用的。
1.4.2 引用类型注入
这样一个场景,比如有一个car对象,user对象里面有一个属性car类型是Car类型,这个时候用注解,首先先对Car使用注解,@Component,然后再对User对象中的car属性,使用@Autowired注解,这个注解是自动装配。
但是这种方式有一个问题,如果有多个类型一致的对象,将无法选择具体注入哪一个对象。可以再使用@Qualifier()注解,该注解告诉spring容器自动装配哪个名称的对象。
还有一种方式是使用@Resource注解
@Resource(name = "car")
private Car car;
这种更推荐
@PostConstruct 指定对象被创建后执行的方法,相当于init-method属性,@PreDestory 对象销毁之前执行的方法,相当于destory-method
2 AOP
2.1 Spring和Junit4整合
首先是导包,4+2+aop+test.
//帮我们创建容器
@RunWith(SpringJUnit4ClassRunner.class)
// 指定创建容器时,使用哪个配置文件
@ContextConfiguration("classpath:applicationContext.xml")
public class Demo {
// 将名为user的对象注入到u变量中
@Resource(name = "user")
private User u;
@Test
public void fun1() {
System.out.println(u);
}
}
使用这种方式,我们就可以不用创建容器。
2.2 spring中的aop
spring实现aop的原理,包括动态代理,cglib代理
动态代理的局限性:被代理对象必须要实现接口,才能产生代理对象,如果没有接口将不能使用动态代理技术
cglib代理:第三方代理技术,cglib代理可以对任何类生成代理,代理的原理是对目标对象进行继承代理,如果目标对象被final修饰,那么该类无法被cglib代理。使用顺序,优先使用动态代理,没有实现接口,则使用cglib代理。下面是两个示例代码:
public class UserServiceProxyFactory implements InvocationHandler {
private UserService us;
public UserServiceProxyFactory(UserService us) {
super();
this.us = us;
}
public UserService getUserServiceProxy() {
// 生成动态代理
UserService usProxy = (UserService) Proxy.newProxyInstance(UserServiceProxyFactory.class.getClassLoader(),
UserServiceImpl.class.getInterfaces(), this);
return usProxy;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub
System.out.println("打开事务");
Object invoke = method.invoke(us, args);
System.out.println("关闭事务!");
return invoke;
}
}
public class UserServiceProxyFactory2 implements MethodInterceptor {
private UserService us;
public UserService getUserServiceProxy() {
// 生成动态代理
Enhancer en = new Enhancer();// 帮我们生成代理对象
en.setSuperclass(UserServiceImpl.class);// 设置对谁进行代理
en.setCallback(this);// 代理要做什么
return (UserService) en.create();// 创建代理对象
}
@Override
public Object intercept(Object proxyobj, Method method, Object[] arg, MethodProxy methodProxy) throws Throwable {
// TODO Auto-generated method stub
// 打开事务
System.out.println("打开事务");
// 调用原有方法
Object returnValue = methodProxy.invokeSuper(proxyobj, arg);
// 提交事务
System.out.println("提交事务");
return null;
}
}
public class Demo {
@Test
public void fun2() {
UserService us = new UserServiceImpl();
// UserServiceProxyFactory2 factory = new UserServiceProxyFactory2();
UserServiceProxyFactory2 factory = new UserServiceProxyFactory2();
UserService usProxy = factory.getUserServiceProxy();
usProxy.update();
}
public void fun1() {
UserService us = new UserServiceImpl();
UserServiceProxyFactory factory = new UserServiceProxyFactory(us);
UserService usProxy = factory.getUserServiceProxy();
usProxy.update();
}
}
2.2.1 aop中的名词
- Joinpoint(连接点):目标对象中所有可以增强的方法(比如上面的save方法)
- Pointcut(切入点):目标对象中已经增强的方法
- Advice(通知/增强):进行增强的代码
- Target(目标对象):被代理对象
- Waving(织入):将通知应用到切入点的过程
- Proxy(代理):将通知织入到目标对象之后,形成代理对象
- aspect(切面):切入点+t通知
2.3 aop演示
2.3.1导包:4+2,然后是spring的aop包和第三方的aop包。
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar和com.springsource.org.aopalliance-1.0.0.jar
导包以后,接着导入aop的约束:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- bean definitions here -->
</beans>
2.3.2 准备好目标对象和通知
public class MyAdvice {
// 前置通知,目标方法运行之前调用
// 后置通知,目标方法运行之后调用(如果出现异常都会调用)
// 环绕通知,在目标方法之前和之后都调用
// 异常拦截通知,如果出现异常就会调用
// 后置通知无论时候出现异常,都会调用(无论是否出现异常都会调用)
// -----------------------------------------------------------
// 前置通知
public void before() {
System.out.println("这是前置通知!");
}
// 后置通知
public void afterReturning() {
System.out.println("这是后置通知,如果出现异常就不会调用");
}
// 环绕通知
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("这是环绕通知!之前的部分");
Object proceed = pjp.proceed();// 调用目标方法
System.out.println("这是环绕通知之后的部分");
return proceed;
}
// 异常通知
public void afterException() {
System.out.println("出事啦!出现异常啦!");
}
// 后置通知
public void after() {
System.out.println("后置通知不出现异常也会调用!");
}
}
2.3.3 配置进行织入,将通知织入目标对象中
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 1.配置目标对象 -->
<bean name="userService" class="com.dlj.service.UserServiceImpl"></bean>
<!--2配置通知对象 -->
<bean name="myAdvice" class="com.dlj.e_annotationaop.MyAdvice"></bean>
<!-- 开启使用注解完成织入 -->
<!-- 3配置将通知织入目标对象 -->
<aop:config>
<!-- 配置切入点
public void com.dlj.service.UserServiceImpl.save()
void com.dlj.service.UserServiceImpl.save()
* com.dlj.service.UserServiceImpl.save()
* com.dlj.service.UserServiceImpl.* //为这个类下面的所有方法都配置切入点,但是是空参
* com.dlj.service.UserServiceImpl.*(..)
* com.dlj.service.*ServiceImpl.*(..) //service包下所有以ServiceImpl结尾的
* com.dlj.service..*ServiceImpl.*(..) //不仅找service包下,同时还有他的子包 -->
<aop:pointcut expression="execution(* com.dlj.service.*ServiceImpl.*(..) )" id="pc"/>
<aop:aspect ref="myAdvice">
<!-- 指定名为before的方法作为前置通知 -->
<aop:before method="before" pointcut-ref="pc"/>
<!-- 后置 -->
<aop:after-returning method="afterReturning" pointcut-ref="pc"/>
<!-- 环绕 -->
<aop:around method="around" pointcut-ref="pc"/>
<!-- 异常 -->
<aop:after-throwing method="afterException" pointcut-ref="pc"/>
<!-- 后置 -->
<aop:after method="after" pointcut-ref="pc"/>
</aop:aspect>
</aop:config>
</beans>
测试代码如下:
public class Demo {
// 将名为user的对象注入到u变量中
@Resource(name = "userService")
private UserService us;
@Test
public void fun1() {
us.save();
}
}