文章目录
一、ioc-控制反转
1.依赖注入
控制反转是目的,依赖注入是实现方式;实际上是工厂模式+反射机制
interface Fruit {
public abstract void eat ();
}
class Apple implements Fruit {
public void eat () {
System.out.println("Apple");
}
}
class Orange implements Fruit {
public void eat () {
System.out.println("Orange");
}
}
class Factory {
public static Fruit getInstance (String ClassName) {
Fruit f = null;
try {
f = (Fruit) Class.forName(ClassName).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return f;
}
}
class Client {
public static void main (String[] a) {
Fruit f = Factory.getInstance("io.github.dunwu.spring.Apple");
if (f != null) {
f.eat();
}
}
}
2.bean的作用域
类别 | 说明 |
---|---|
singleton | 单例模式,ioc容器中只存在一个对象,默认值 |
propertype | 每次调用创建一个对象 |
request | 每次http请求创建一个bean,作用于webApplicationContext环境 |
session | 同一个http session共用一个bean,不同session使用不同的bean,同样作用与webApplicationContext环境中 |
globalSession | 作用与portlet环境,仅适用于webApplicationContext环境中 |
比较常用的是singleton和propertype;像singleton,由于共用一个bean,由IOC容器来管理bean的生命周期和状态,不需要反复的创建与销毁,因此性能更高;propertype模式的话,每次请求相当于new了一次实例,一旦创建成功,容器不在跟踪实例,也不会维护Bean实例的状态。
那么什么时候时候使用singleton什么时候使用propertype呢?正常情况下考虑性能问题都使用默认的singleton,但是在要考虑线程安全问题的时候则使用propertype,比如bean中有非静态变量的时候,就使用propertype;如:
@service
public class test{
private num = 0;
.........
}
3.bean的生命周期
首先生成bean的描述,然后通过bean工厂后值处理器,处理bean的相关属性,然后进行bean的初始化,其中BeanPostProcessor前和后的操作,然后bean就ok了,容器关闭,destory方法销毁;如下图:
二、aop-面向切面编程
springAOP是通过动态代理模式来实现的;动态代理有两种实现,jdk proxy和cglib;
1.jdk代理模式
public class JDKDynamicProxy implements InvocationHandler {
//被代理的目标对象
private Object proxyObj;
/**
* Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
* loader :类加载器 一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
* interfaces:一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
* h :一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上
*/
public Object newProxy(Object proxyObj){
this.proxyObj = proxyObj;
//返回一个代理对象
return Proxy.newProxyInstance(proxyObj.getClass().getClassLoader(),
proxyObj.getClass().getInterfaces(),
this);
}
/**
* 执行目标对象
* Object proxy:被代理的对象
* Method method:要调用的方法
* Object args[]:方法调用时所需要的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
before();
Object object = method.invoke(this.proxyObj,args); // 通过反射机制调用目标对象的方法
after();
return object;
}
public void before(){
System.out.println("开始执行目标对象之前...");
}
public void after(){
System.out.println("开始执行目标对象之后...");
}
}
2.cglib代理模式
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class CGLIBProxy implements MethodInterceptor{
private Object targetObject ;//被代理的目标对象
public Object createProxyInstance(Object targetObject) {
this.targetObject = targetObject;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(targetObject.getClass());// 设置代理目标
enhancer.setCallback(this);// 设置回调
return enhancer.create();
}
/**
* 在代理实例上处理方法调用并返回结果
* @param object : 代理类
* @param method :被代理的方法
* @param args :该方法的参数数组
* @param methodProxy
*/
@Override
public Object intercept(Object object, Method method, Object[] args,
MethodProxy methodproxy) throws Throwable {
Object result = null;
try {
System.out.println("前置处理开始 ...");
result = methodproxy.invoke( targetObject , args);//执行目标对象的方法
System.out.println("后置处理开始 ...");
} catch (Exception e) {
System.out.println("异常处理 ...");
} finally {
System.out.println("调用结束 ...");
}
return result;
}
}
3.两种代理模式对比
1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP
3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换
注:JDK动态代理要比cglib代理执行速度快,但性能不如cglib好。
三、spring注解
@Cacheable-spring的缓存注解
@Cacheable能干什么?
为了通俗易懂的理解,举个栗子:一个方法,getBooksByUsernameAndLanguage(String username, int language),显然,是一个获取数据库里所有我的英文书对象的方法,返回应该是一个列表。如果这个函数的返回值很大,而且会在页面上被经常调用,那么每一次调用都要重新连接数据库并返回一个数据量庞大的list,可能页面响应和资源占用会比较大。而我们希望的是,第一次调用这个方法时,返回的数据能被放到服务器端的缓存里,以便于后面要调用这个方法时,能直接从缓存里取到,这样就不用再查数据库占用资源了。而@Cacheable的作用就是这个。
@Cacheable(value = "CACHE_BOOK",key = "#username", condition = "#language = 1")
public List<Book> getBooksByUsernameAndLanguage(String username, int language) {
// balabalabala...里面的代码不重要
return bookList;
}
看code,@Cacheable注解只有三个属性。
value : 必须要的。就是个自己取的名字,通过它指明了第一次调用这个方法时返回的bookList将被存在内存的哪里。
key : 可选。要使用SpEL表达式,这里与参数username对应,当传入的username值变了的话就不去取缓存里的数据了,而是执行getBooksByUsernameAndLanguage方法。(这是必须的,因为username变了,返回值也就变了,缓存里的数据不符合了,因此这个选项很重要)。spring默认用方法的签名来当做key。
condition:方法返回的结果bookList,要不要缓存起来?condition就添加了一个限定条件。这个例子中,只有传入的语言代码是1,返回的bookList才会被缓存起来,如果给language传了别的值,那么bookList是不会缓存起来的。
注入相关-@Autowired @Resource @Qualifier
1.@Autowired:
@Autowired : 默认是以byType按类型自动注入。
@Autowired + @Qualifier(“名称”):将按照名称自动注入
2.@Resource:
@Resource() 如果没有指定name属性,当注解写在字段上时,默认取字段名进行按照名称注入,
如果注解写在setter方法上默认取属性名进行注入。
当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。
@Resource(name="") 将按照名称自动注入