复习:
Spring贯穿了整个三层架构
对象的容器,管理项目中的所有对象
设计理念
IOC
控制反转 -> 思想
创建对象的方式被反转了, 原来由java程序自己创建对象, 自己管理依赖
用了spring之后, 由spring创建对象, 注入依赖关系. 我们只需要编写配置文件即可
DI技术: 依赖注入,它是ioc思想的主要实现方式
方式:
1. 属性注入
2. set注入
3. 构造器注入
AOP
面向切面(后面有详细介绍)
详细配置
配置文件: 位置任意, 名字任意
建议写成 src/applicationContext.xml
<bean>元素的配置
id: 取出对象使用的字符序列, 1-不能重复 2-不能有特殊字符 (旧属性,基本废弃)
name: 取出对象使用的字符序列 1-能重复 2-能有特殊字符 (新属性)
class: 完整类名
生命周期方法配置
init-method : 创建对象后调用
destroy-method : 对象被销毁前调用
scope属性:
singleton : 单例模式, 容器创建时就会创建对象, 每次都是同一个对象
prototype : 多例模型, 每次获取才会创建对象, 每次都是一个新的对象
【注解方式配置】
可以看以下这篇博主的文章,写的详细
https://blog.csdn.net/u010648555/article/details/76299467
1. 导包
4 :expression,beans,context,core
2 :2个日志jar包
1 : spring-aop-4.2.4.RELEASE.jar(aop的jar包)
2. 配置src/applicationContext.xml
在顶部添加xmlns:context="http://www.springframework.org/schema/context"
在xsi:schemaLocation属性中添加2行网址
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
添加的context约束
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
添加的context约束
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 添加context标签,用于扫描指定的包和他的子孙包,若包下的类有注解,就构造对象,并放到spring容器 -->
<context:component-scan base-package="com.bwf.bean" />
<!-- 如果搜索路径:base-package="com.bwf",那么扫描的就是com.bwf下所有的包-->
</beans>
3. 根据类上的注解@Component("user") 中的名字
取出user对象
组件
一下四个都能作为spring注解,可以划分不同层之间的实例,方便区分
@Component :标准一个普通的spring Bean类。
@Repository:标注一个DAO组件类。
@Service:标注一个业务逻辑组件类。
@Controller:标注一个控制器组件类。
注入属性
@Value("李四")
private String name;
或
@Value("李四")
public void setName(String name) {
this.name = name;
}
引用类型注入
方式1: 根据类型自动装配
@Autowired
private Car car;
方式2: 根据类型和name装配
若有2个相同类型,则需要额外指定容器中的对象名
@Autowired
@Qualifier("car2")
private Car car;
方式3: 根据name获取
@Resource(name="car2")
private Car car;
推荐使用:@Resource注解在字段上,这样就不用写setter方法了,并且这个注解是属于J2EE的,减少了与spring的耦合。这样代码看起就比较优雅。
多例配置
@Scope("prototype")
public class User{
package com.bwf.a_annotation;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.bwf.bean.User;
public class Demo1 {
@Test
public void fn1(){
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
User u1 = (User) ac.getBean("user");
User u2 = (User) ac.getBean("user");
System.out.println(u1 == u2);
ac.close();
}
}
注意:如果调用了close方法销毁对象,那么多例配置生成的对象不会被销毁,只有单例生成的对象才会被销毁
【spring整合junit单元测试】
1. 导包
spring-test-4.2.4.RELEASE.jar
2. 写一个测试类
package com.bwf.b_test;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.bwf.bean.User;
//让j测试运行于spring运行环境
@RunWith(SpringJUnit4ClassRunner.class)
//Spring整合JUnit4测试时,使用注解引入配置文件
@ContextConfiguration("classpath:applicationContext.xml")
public class Demo1 {
//3. 把测试类中的成员属性用容器中的对象赋值
//@Autowired容器内的同一个类的对象只有一个时,可以用自动装配
//有多个对象可以用resource
@Resource(name="user")
private User u;
// 4. 在测试方法中使用
// @Test
@Test
public void fn1(){
System.out.println(u);
}
//可以有多个
@Test
public void fn2(){
System.out.println(u);
}
}
【AOP思想的介绍】
Aspect Oriented Programming
面向切面编程: 面向对象思想的一种升级
横向重复代码, 纵向提取
生活中的例子
老师的笔记中出了一个小错
1. 每个同学自己改一下
2. 老师改一下 (更好的方案)
开发中的案例
1. 用过滤器解决所有Serlvet的编码问题
(装饰模式强化过的getParameter方法,去除get方法产生的中文乱码问题)
https://blog.csdn.net/qq_36194262/article/details/83507615
2. 用动态代理解决service中所有方法的打开事务, 提交事务出现异常回滚事务的操作
【Spring AOP】
Spring本质就是个容器, 帮我们创建对象
所以它可以在创建对象时, 根据我们的要求直接生成代理对象
【生成方式】
1. 动态代理
java的原生动态代理
概念参考自
https://www.cnblogs.com/xdp-gacl/p/3971367.html
package com.bwf.c_proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import com.bwf.service.UserService;
public class UserServiceProxyFactory implements InvocationHandler {
/*本类用来强化userservice方法,主方法通过构造本类对象,
传入原本的对象,返回的是增强过的对象*/
private UserService us;
//传入原本的对象,准备强化
public UserServiceProxyFactory(UserService us) {
super();
this.us = us;
}
/**
* 获得us的代理对象 具有事务的功能
* @return
*/
public UserService getProxy(){
/*newProxyInstance这个方法有三个参数,第一个是被代理对象的类加载器,第二个是该类的接口,所以说被代理的对象必须要实现接口,第三个参数是InvocationHandler 接口的实现类
这里很巧妙,通过当前类实现InvocationHandler接口,重写invoke方法(写你要增强的代码),这时候第三个参数就可以传this(当前对象,因为我实现了InvocationHandler接口)*/
Object proxy = Proxy.newProxyInstance(us.getClass().getClassLoader()
, us.getClass().getInterfaces(), this);
return (UserService) proxy;
}
/*
* 在invoke方法编码指定返回的代理对象干的工作
* proxy : 代理对象
* method: 代理方法调用的对象
* args: 方法参数
*
* 当调用代理对象执行方法时,
* 实际上执行的都是invoke方法里面的代码,
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("打开事务");
Object res = method.invoke(us, args);
System.out.println("提交事务");
return res;
}
}
构造该类对象传入userservice对象,调用getproxy方法即可获得增强过的userservice
2. cglib代理
package com.bwf.c_proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import com.bwf.service.UserService;
import com.bwf.service.UserServiceImpl;
public class UserServiceProxyFactory2 implements MethodInterceptor {
/**
* cglib的代理方式
* 获得us的代理对象 具有事务的功能
* @return
*/
public UserService getProxy(){
// 帮我们生成代理对象
Enhancer enhancer = new Enhancer();
// 设置对谁进行代理
enhancer.setSuperclass(UserServiceImpl.class);
// 设置代理要做什么
enhancer.setCallback(this);
// 生成代理对象
UserService us = (UserService) enhancer.create();
return us;
}
/**
* 原始对象 obj
* 原始方法 method
* 原始参数 args
* 代理方法 methodProxy
*/
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("打开事务");
Object res = methodProxy.invokeSuper(obj, args);
System.out.println("提交事务");
return res;
}
}
构造该对象,调用getproxy方法即可获得增强过的userservice
使用规则: 如果能用动态代理, 就用动态代理; 否则就用cglib代理
【AOP名词】
中文名 | 英文名 | 解释 |
连接点 | Join point | 目标对象中, 可以增强的方法 |
切入点 | Pointcut | 目标对象中, 已经增强的方法 |
通知 | Advice | 增强的代码 |
目标对象 | Target object | 被代理的对象 |
织入 | Weaving | 将通知应用到切入点的过程 |
代理 | AOP proxy | 将通知织入切入点, 最后形成代理 |
切面 | Aspect | 通知 + 切入点 |
【spring aop 配置】
注意:aop页用xml文件配置,当然也可以用注解,但是用注解太麻烦,每一个要配置的类都要写注解.
1. 导包
4个核心包(core,expression,beans,context)
2个新老日志jar包
2个在srping的lib中的包
spring-aop-4.2.4.RELEASE.jar
spring-aspects-4.2.4.RELEASE.jar
2个(都在spring依赖包中):
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
位置:spring-framework-3.0.2.RELEASE-dependencies\org.aspectj\com.springsource.org.aspectj.weaver\1.6.8.RELEASE
com.springsource.org.aopalliance-1.0.0.jar
位置:spring-framework-3.0.2.RELEASE-dependencies\org.aopalliance\com.springsource.org.aopalliance\1.0.0
2. 书写目标对象和通知
UserServiceImpl类用于产生目标对象
package com.wowowo.service.impl;
import com.wowowo.service.UserService;
public class UserServiceImpl implements UserService {
@Override
public void insert() {
System.out.println("增");
}
@Override
public void delete() {
System.out.println("删");
//会抛异常
int x = 1 / 0;
}
}
通知写到一个MyAdvice类里面
注意:
通知的类型 | 解释 |
before | 前置通知(方法执行前) |
after-returning | 后置通知(一旦出现异常, 就不会出现) |
after-throwing | 后置通知 (出现了异常, 才会出现) |
after | 后置通知 (就算出现异常, 也会出现) |
around | 环绕通知(包裹在原来方法的前后出现) |
* 环绕通知的方法必须声明为
public Object around(ProceedingJoinPoint pjp) throws Throwable
并通过Object retVal = pjp.proceed();调用原来的方法
package com.wowowo.springaop;
import org.aspectj.lang.ProceedingJoinPoint;
public class MyAdvice {
//方法调用前执行
public void before() {
System.out.println("before");
}
//方法调用后执行,方法出现异常就不执行
public void afterReturning() {
System.out.println("afterReturning");
}
//方法出现异常时执行
public void afterThrowing() {
System.out.println("afterThrowing");
}
//无论是否出现异常,都会在方法结束后执行
public void after() {
System.out.println("after");
}
// 环绕通知
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("打开事务");
// 调用原来的方法
Object retVal = pjp.proceed();
System.out.println("提交事务");
return retVal;
}
}
3. ApplicationContext.xml中配置目标对象, 通知, 织入
<?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"
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
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean name="us" class="com.wowowo.service.impl.UserServiceImpl">
</bean>
<bean name="ad" class="com.wowowo.springaop.MyAdvice">
</bean>
<aop:config>
<!-- 切面:引用通知的id -->
<aop:aspect ref="ad">
<!-- 切入点: 写表达式去筛选连接点 -->
<aop:pointcut id="pc"
expression="execution(* com.wowowo.service.impl.*ServiceImpl.*(..))" />
<!-- 织入: 把通知织入到切入点中 . 需要切入点id 和通知中的方法名 -->
<!-- 前置通知 -->
<aop:before pointcut-ref="pc" method="before"/>
<!-- 后置通知 (一旦异常就没有了) -->
<aop:after-returning pointcut-ref="pc" method="afterReturning"/>
<!-- 异常通知 -->
<aop:after-throwing pointcut-ref="pc" method="afterThrowing"/>
<!-- 后置通知(就算异常也会有) -->
<aop:after pointcut-ref="pc" method="after"/>
<!-- 环绕通知 -->
<aop:around pointcut-ref="pc" method="around"/>
</aop:aspect>
</aop:config>
</beans>
userService类:
package com.bwf.service;
import org.springframework.stereotype.Component;
@Component("us")
public class UserServiceImpl implements UserService {
@Override
public void delete() {
System.out.println("删");
}
@Override
public void update() {
System.out.println("改");
//会抛出异常:java.lang.ArithmeticException: / by zero
int x = 1/0;
}
}
配置完成可以写个测试单元进行一下测试
package com.wowowo.springaop;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.wowowo.service.UserService;
import com.wowowo.service.impl.UserServiceImpl;
//
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:com/wowowo/springaop/applicationContext.xml")
public class Demo01 {
@Autowired
//注解方式自动装配userservice
private UserService us;
//springaop方式实现
@Test
public void test1() {
us.insert();
}
@Test
public void test2() {
us.delete();
}
}
test1和test2方法被强化后的区别:
- test1方法没有异常,会调用afterreturning方法
- test2方法会抛异常,会调用afterthrowing方法