复习
1 spring介绍
- spring是一个轻量级J2EE开发框架,用来管理bean的一个容器.由单元测试,
核心容器
,AOP
,DAO,ORM,JDBC,TX,Web,MVC等部分组成, - 我们主要学习使用两个核心内容: IOC,AOP
2 ioc是什么
- IOC控制反转,是指将创建对象的控制权反转给spring容器
- spring容器其实就是配置文件applicationContext.xml
- 一个标签就会创建一个对象
<bean id="" class="">
3 di是什么
- DI依赖注入,属性赋值
- set方法注入
- 要赋值的属性,得有对应的set方法
- 具体是在bean标签内部使用
<property name="属性名" value="基本类型值" ref="引用类型,引用另一个对象的id">
- 构造方法注入
- 自动注入
4 自动注入是怎么注入的
- 需要先在bean标签中设置自动注入的属性autowire
- 指定自动注入的方式autowire=“byType|byName”
- byType
- 例如: UserServiceImpl中需要注入UserDao类型的属性
- spring容器中有该UserDao类型的bean,就会注入
- 但是!!!如果容器中有不止一个该UserDao类型的bean,就会注入失败!!
- byName
- 此时就可以使用byName来指定,注入哪个UserDao类型的bean
- 容器中UserDao的id与UserServiceImpl类中UserDao属性名一致
bean的细节
IOC是可以控制创建对象时是: 单列还是多例
单例: 单实例,即单对象,也就是说这个类有且仅有一个对象
多例: 多实例,即多个对象,也就是说这个类可以有多个对象
<bean id="..." class="..." scope="singleton|prototype">
<!--
IOC,默认创建对象是单实例(scope="singleton"),即每次从容器中获得的是同一个对象
设置成scope="prototype",那就多实例,即每次从容器获得都是新对象
-->
<bean id="myClass" class="com.qf.model.MyClass" scope="prototype">
<property name="age" value="18"/>
<property name="name" value="京超啊"/>
</bean>
@Test
public void testIOC(){
String path = "applicationContext.xml";
// 通过配置文件,创建出容器
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(path);
// 从容器获得对象
MyClass myClass1 = context.getBean("myClass", MyClass.class);
System.out.println(myClass1.hashCode());
// 如果是单例,每次都是同一个地址
MyClass myClass2 = context.getBean("myClass", MyClass.class);
System.out.println(myClass2.hashCode());
// 如果是多例,每次地址都不一样
MyClass myClass3 = context.getBean("myClass", MyClass.class);
System.out.println(myClass3.hashCode());
}
3.注解开发
以前就可以还使用@WebServlet和@WebFilter注解,省去web.xml中写的配置
以后工作实际开发中,都会使用注解开发,效率很高!
IOC也要变成注解开发,用来省去在applicationContext.xml中的配置
注解 | 解释 |
---|---|
@Controller | 创建对象,用在控制层 |
@Service | 创建对象,用在业务层 |
@Repository | 创建对象,用在数据访问层 |
@Component | 创建对象,其他地方用 |
@Scope | 控制对象的单例|多例 |
以上注解可以取代配置文件中的<bean>标签
DI相关注解
注解 | 解释 |
---|---|
@Value | 给基本类型注入 |
@Autowired | 给引用类型自动注入(默认按照类型注入)byType |
@Resource | 给引用类型自动注入(默认按照名字注入)byName |
@Qualifier | 和@Autowired联用,可以实现按名字注入 |
以上注解可以取代配置文件中的<property>标签
@Service // 相对于是<bean id="" class="">标签,默认id是当前类名首字母小写
public class UserServiceImpl implements UserService {
@Autowired // 相对于是autowire=byType属性
private UserDao userDao;
public void findUserById() {
System.out.println("UserServiceImpl--->业务层执行" );
userDao.findUserById();
}
}
开启注解扫秒
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 扫描注解,得让容器知道哪些类需要由Spring创建对象 -->
<context:component-scan base-package="com.qf"/>
</beans>
测试
@Test
public void testIOCandDIByAnno(){
String path = "applicationContextByAnno.xml";
// 通过配置文件,创建出容器
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(path);
// 使用注解,默认id即对象名,是类名首字母小写
UserService userService = context.getBean("userServiceImpl", UserService.class);
userService.findUserById();
}
注解使用细节
IOC创建对象的注解,四个功能一样,都可以创建对象,只不过建议不同的位置使用不同的注解,见名知意
加上创建对象的注解后,默认对象名是
类名首字母小写
,即需要通过类名首字母小写从容器中获得对象其实可以在注解中设置参数,给对象取名.例如@Service(“aaa”),以后就用aaa从容器中取值
自动注入时@Autowired ,默认
按照类型
注入但是如果容器中有两个该类型的bean,自动注入就会失败,此时可以
按名字注入
@Resource(name="userDaoImpl2")
如果不使用@Resource按名字注入,也可以使用
@Qualifier("userDaoImpl2")
配合@Autowired一起实现,按照名字注入可以给类加@Scope(“prototype|singleton”) 来控制单例多例
3 代理设计模式
代理的设计理念是限制对象的直接访问,即不能通过 new 的方式得到想要的对象,而是访问该对象的代理类。
这样的话,我们就保护了内部对象,如果有一天内部对象因为 某个原因换了个名或者换了个方法字段等等,那对访问者来说 一点不影响,因为他拿到的只是代理类而已,从而使该访问对 象具有高扩展性。
代理类可以实现拦截方法,修改原方法的参数和返回值
,满足 了代理自身需求和目的,也就是代理的方法增强
性。
按照代理的创建时期,代理可分为:静态代理
和动态代理
。
静态代理由开发者手动创建,在程序运行前,已经存在;
动态代理不需要手动创建,它是在程序运行时动态的创建代理类。
总结:代理模式–给某个目标对象提供一个代理,以改变对该对象的访问方式,以便于对目标方法的增强
3.1 静态代理
静态代理,在运行之前,提前先把代理创建好.
需求: 目标类(Fangdong),目标方法(chuzu()),来一个代理FangdongProxy(中介),中介会在目标方法执行前后,实现一些增强的功能.
目标接口和实现类
public interface Fangdong {
void chuzu();
}
public class FangdongImpl implements Fangdong{
public void chuzu() {
System.out.println("房东出租房!" );
}
}
代理
public class FangdongProxy {
private Fangdong fangdong;
public FangdongProxy(Fangdong fangdong){
this.fangdong = fangdong;
}
// 代理执行方法
public void chuzu(){
// 前: 功能增强
System.out.println("租房前: 中介发布消息");
// 目标方法:出租房
fangdong.chuzu();
// 后: 功能增强
System.out.println("中介签合同/后期水电气的维修" );
}
}
测试
public static void main(String[] args) {
// 找代理
FangdongProxy proxy = new FangdongProxy(new FangdongImpl( ));
proxy.chuzu();
}
租房前: 中介发布消息
房东出租房!
中介签合同/后期水电气的维修
3.2 动态代理
动态代理: 在程序运行过程中,动态的为目标类产生代理类
实现动态代理有两种方案:
jdk动态代理(JDK自带)
-jdk动态代理,只能代理接口,即目标类必须有接口
cglib动态代理
(第三方技术,需要导入第三方jar包)cglib动态代理,目标类可以是接口也可以是实现类
3.2.1 JDK动态代理
在java的java.lang.reflect.InvocationHandler包下有一个InvocationHandler接口,可以实现动态产生代理对象.
public class MyInvocationHandler implements InvocationHandler {
private Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
/**
* @param proxy 代理对象
* @param method 目标方法
* @param args 目标方法执行需要的参数
* @return 目标方法执行后返回值
* @throws Throwable
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 目标方法前:
System.out.println("前期宣传" );
// 目标方法执行
Object ret = method.invoke(target,args);
// 目标方法后:
System.out.println("后期维护" );
return ret;
}
}
测试
public class TestJDKDynamic {
public static void main(String[] args) {
// 动态代理,只需要给定目标类,就会产生目标类的代理对象
// Class clazz = FangdongImpl.class;
// ClassLoader loader = clazz.getClassLoader( );
// Class[] interfaces = clazz.getInterfaces( );
// MyInvocationHandler handler = new MyInvocationHandler(new FangdongImpl( ));
// /**
// * @param loader 被代理的类(目标类)的类加载器
// * @param interfaces 被代理的类(目标类)的实现的接口数组
// * @param handler 刚才自己创建的MyInvocationHandler
// * @return 返回代理对象
// */
// Fangdong proxy = (Fangdong) Proxy.newProxyInstance(loader, interfaces, handler);
//
// proxy.chuzu();
Class clazz = CarFactoryImpl.class;
ClassLoader loader = clazz.getClassLoader( );
Class[] interfaces = clazz.getInterfaces( );
MyInvocationHandler handler = new MyInvocationHandler(new CarFactoryImpl( ));
/**
* @param loader 被代理的类(目标类)的类加载器
* @param interfaces 被代理的类(目标类)的实现的接口数组
* @param handler 刚才自己创建的MyInvocationHandler
* @return 返回代理对象
*/
CarFactory proxy = (CarFactory) Proxy.newProxyInstance(loader, interfaces, handler);
proxy.sellCar();
// JDK的动态代理,目标类必须实现接口,否则代理失败
}
}
3.2.2 CGLIB动态代理
CGLIB是第三方技术,需要导入第三方jar包
但是Spring框架已经整合了CGLIB技术,即在导入spring依赖时,已经导入了cglib包了
public class MyProxyInterceptor implements MethodInterceptor {
// cglib增强器
private Enhancer enhancer = new Enhancer();
public MyProxyInterceptor(Class targetClass){
enhancer.setSuperclass(targetClass);
enhancer.setCallback(this);
}
// 获得代理对象
public Object getProxyBean() {
return enhancer.create();
}
/**
* @param target 目标对象(被代理对象)
* @param method 目标方法
* @param args 目标方法的参数
* @param methodProxy 代理目标方法
* @return 目标方法执行后,返回数据
* @throws Throwable
*/
public Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
// 前:
System.out.println("前期宣传" );
// 目标方法
Object ret = methodProxy.invokeSuper(target, args);
// 后:
System.out.println("后期维护" );
return ret;
}
}
测试
public class TestCGLIB {
public static void main(String[] args) {
// 动态代理,只需要给定目标类,就会产生目标类的代理对象
// MyProxyInterceptor myProxyInterceptor = new MyProxyInterceptor(FangdongImpl.class);
// Fangdong proxy = (Fangdong) myProxyInterceptor.getProxyBean( );
// proxy.chuzu();
MyProxyInterceptor myProxyInterceptor = new MyProxyInterceptor(CarFactoryImpl.class);
CarFactoryImpl proxy = (CarFactoryImpl) myProxyInterceptor.getProxyBean( );
proxy.sellCar();
// CGLIB代理的好处,就在于目标类即可以实现接口,也可以不用实现接口,都可以代理
}
}
下一节springAOP
问:spring代理和通知的关系
答:aop的底层原理就是动态代理(JDK,CGLIB)