什么是Spring,为啥要学Spring?
Spring是开源(遵守GPL协议)的轻量级(可插可拔,可有可无)框架。它以IOC即控制反转(InverseOf Control)和AOP(AspectOriented Programming)为核心,其目的是简化企业级应用开发的难度。
Spring的优点
Ø 开源免费
Ø 解藕,方便开发和维护
Ø 强大的AOP的支持,可以在不改变方法代码前提下,动态改变方法执行的行为
Ø 集成其它优秀框架
Ø 声明式事务管理(以AOP切入的事务编程式事务:需要手动开启和提交的事务)
Ø 提供对JavaEE的支持,封装常用的JavaEE API (HibernateTemplate -简单封装了hoibernate操作的相关APIJDBCTemplate Redislemplate--操作Redis的简单封装),降低开发难度
使用Spring
官网:http://spring.io
spring下载:http://repo.spring.io/release/org/springframework/spring/
目录结构
Spring核心:IOC
IOC:Inverse Of Control,将对象的创建权交给(反转给)Spring容器进行管理。它由bean和DI(DenpendencyInjection)组成。在spring里,所有java类都称为bean,每一个java对象里都成员(属性、依赖),我们给这个成员注入(初始化值)的过程称为“依赖注入”。
IOC的实现(XML方式)
创建一个web工程
创建配置文件
spring-framework-5.0.1.RELEASE\docs\spring-framework-reference下的index.html里,点击core
<?xmlversion="1.0" encoding="UTF-8"?>
<beans xmlns(xml命名空间)
xmlns:xsi(当前文档遵守的规范)="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation(约束scheme的位置)="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>
spring容器在启动时,需要加载日志系统,所以需要依赖commons-logging-xxx.jar,而这个jar,spring官方并没有提供,需要自己下载
测试
依赖注入
spring通过反射创建bean实例,并且将对应的属性值注入这个类。
普通属性
引用其它bean对象
这两种方式如何选择呢?第一种写法更好!声明的这个bean可以被其它bean所引用,如何要注入的bean成员(属性)比较少时,我们直接使用第二种方式。
其它类型的注入
Ø Array
Ø List
Ø Map
Ø Properties
Ø 构造器的注入
bean的id和name的问题
Ø id:id是bean的标识,它要求唯一,并且遵守XML对ID的约束,它的命名规则:以字母开头,后面可以加数字、下划线、句号、逗号;
name:bean的名称,它没有任何命名约束,并且可以重复,如果命名重复,则最后一个生效。如果一个bean没有id,spring将使用name作为其标识。
IOC的注解实现
在spring,使用如下注解可以将类注册为spring bean
@Component @Controller @Service @Repository
@Component
泛指组件,任何类加上这个注解,spring都会将其注册成spring bean。
@Controller
标识一个控制层的bean,它是MVC(Controller)的一个细化实现,用于做控制器。
@Service
标识一个业务层bean,它是MVC(Model的细化)里的一个细化实现,用于业务处理。
@Repository
标识一个业务层bean,它是MVC(Model的细化)里的一个细化实现,用于数据访问处理。
Ø Spring使用如上四个注解来实现IOC,那么它们如何选择呢?
从效果上来说,以上四个注解是完全等价的!!!@Component泛指组件,相当于<bean class="xx.x"></bean>,此种方式在你无法确定当前这个类到底属于MVC里的哪一层时,使用此注解;后三个注解只是在语义上对其用途作了一个说明,spring推荐我们尽可能使用后三个注解,因为spring在之后的版本,可能会对后三个注解添加其它功能。
依赖注入(注解)
@Resource @Autowired
我们告诉spring,去哪里找这些注解对应的类
配置包扫描路径
扫描路径的写法有两种:多个包之间以“,”分隔;多个包之间使用多个
< context:component-scan>
Ø Spring使用@Resource或@Autowired两个注解做依赖注入,它们有何区别?
@Resource是javax下的包,不是spring的注解,而@Autowired是spring的注解。
@Resource默认优先以名称加载这个bean,如果名称找不到或没有指定,按照类型加载;
@Autowired默认根据类型加载bean,如果类型找不到或类名重复,需要指定名称,需要借助@Qualifier("xxx")
Spring核心:AOP
AOP:即Aspect OrientedProgramming面向切面编程。Spring使用代理模式实现横向抽取(在不影响程序代码前提下,对目标方法动态添加增强代码来实现对目标方法的增强,它可插可拔,可有可无!)的方式,取代了传统的纵向体系(继承、多态)中的重复代码。spring使用代理模式实现横向抽取,自spring2.5以后,将AspectJ作为AOP的核心实现,AspectJ是一个开源的,专门致力于AOP的框架,它能在不更改程序代码的前提下,动态为目标方法或属性添加增强代码,它是java语言的扩展。
Ø Spring使用代理模式实现“横向抽取”,那么它如何来做?
AspectJ+java动态代理+cglib代理!!!
Java里的代理
在现实生活中,某企业想请刘德华做一个励志演出,一位老总去请刘德华,他能否见到刘本人呢?不能!见到的是刘的代理人(经纪人)!那么代理人能做哪些事情呢?在刘表演前,收首付款、定时间;在刘表演后,收尾款、安排下次活动事宜。经纪人和刘共同完成演出,真正的演出是刘德华表演的,是经纪人“调用了”刘德华!
在以上案例中,经纪人充当了代理人,代理人和被代理人的目标是一样的~但是代理人做的事情要比被代理人多!他是对被代理人进行了增强(扩展)。
普通(静态)代理
代理对象和目标对象(被代理的对象),它们有一个相同的目标,在java实现里,这个“相同的目标”就是一个接口,它们同时实现了此接口。
接口:代理对象和目标对象需要共同实现的目标方法
明星:只做表演的事情
代理人(经纪人):对明星表演这个事情做了一个增强/扩展
老总:调用经纪人来表演
静态代理实现思路是:代理对象和目标对象要共同一个接口,而调用者调用的是代理对象,这个代理通过代理对象调用目标对象并对其添加增强代码完成代理的工作。此种方式如果接口内容发生改变,那么这两个子类都将发生改变。
动态代理
动态代理是代理对象“跟着”目标对象实现某接口的某方法,因为这个代理对象并没有自己实现接口,它是跟着目标对象一起的,所以被称为“动态”。上面的例子中,明星做什么,代理人就跟着做什么!
/**
* 获取代理对象的实例
*
* @param target
* @return
*/
public static Object getProxyInstance(StarPerform target) {
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
new InvocationHandler() {
/**
* invoke就是目标对象要调用的方法
* 就是代理对象要做的事情
* proxy:代理对象
* method:被代理的方法
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("收首款");
System.out.println("定时间");
System.out.println("==============我是华丽分隔线=============");
Object obj = method.invoke(target, args);
System.out.println("==============我是华丽分隔线=============");
System.out.println("收尾款");
System.out.println("安排下一个演出");
return obj;
}
});
}
调用
动态代理不需要去实现某接口,它是跟着目标对象的父接口来做自己的代理。它要求目标对象必须实现一个接口,代理对象跟着目标去实现这个接口,而这个实现是由JDK的Proxy动态[z1]
但是,在我们程序开发,某类是没有实现任何接口的,能不能做代理呢?
Cglib代理
cglib:Code Generation Library,它是一个高效的、开源的代码生成类库。cglib代理又被称为“子类代理”,它的核心实现是在内存里创建一个目标类的子类,通过子类重写父类方法的方式来达到增强目标类方法的目的。
/**
* (目标对象,被代理对象)子类实例
*
* @param target
* @return
*/
public static Object getProxyInstance(StarPerform target) {
class callback implements MethodInterceptor {
/**
* 代理要做的事情
*/
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy proxymethod) throws Throwable {
System.out.println("收首款");
System.out.println("定时间");
System.out.println("==============我是华丽分隔线=============");
Object obj = method.invoke(target, args);
System.out.println("==============我是华丽分隔线=============");
System.out.println("收尾款");
System.out.println("安排下一个演出");
return obj;
}
}
// 用来创建子类的一个工具类
Enhancer en = new Enhancer();
// 设置父类,要给哪一个类创建子类
en.setSuperclass(target.getClass());
// 回调函数:代理要做的事情
en.setCallback(new callback());
// 返回创建的子类对象(代理对象)
return en.create();
}
调用
Spring使用代理实现的AOP,那么它如何选择使用何种代理呢?
如果目标类实现了某接口,spring将使用动态代理(默认),如果目标类没有实现任何接口,将使用cglib代理,或强制告诉spring,使用cglib代理:proxy-target-class=true
SpringAOP使用(XML)
aop底层依赖aspectj,但是spring没有将这个依赖jar加进来,需要自己下载
AOP几个概念
Aspect
切面,把一个面包一切为二,这个面就是切面
Join point
连接点,在spring,所谓的连接点就是被拦截到方法
Advice
通知/增强,通过对目标类织入增强代码,达到扩展目标类方法的目的
Pointcut
把一个面包一切为二,下刀点就是切点,这个切点就是连接点的定义,由切点表达式定义
Introduction
引介,是一个种特殊的通知,它能在程序运行期间动态的为目标添加字段或方法
Target object
被代理的目标对象
AOP proxy
代理对象,通过对目标对象织入增强代码达到扩展目标对象方法的目的
Weaving
织入,有一个附加的意思,用来对目标类实现“可插可拔”的扩展
AOP实现
切点表达
组成:execution(返回值类型完整类名.方法名(参数列表))
常见的切点表达式
指定类下的所有方法
execution(* xxx.指定类.*(..))
指定包下的所有方法
execution(* 指定包.*.*(..))
指定包及其子包下的所有方法
execution(* 指定包..*.*(..))
所有public方法
execution(public * * (..))
所有以get开头的方法
execution(* get*(..))
SpringAOP的注解实现
proxy-target-class="false",它的意思是默认使用接口代理(动态代理),如果将值设置为true,将强制使用cglib代理
切面声明