Spring是什么
*Spring是一个开源框架,为了解决企业应用开发的复杂性而创建的,但现在已经不止应用于企业应用
*是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架
——从大小与开销两方面而言SPringle都是轻量的
——通过控制反转(IoC)的技术达到松耦合的目的
——包含并管理应用对象的配置和生命周期,这个意义上是一种容器
——将简单的组件配置,组合成为复杂的应用,这个意义上是框架
Spring带来了复杂JavaEE开发的春天
什么是框架
框架的特点
——半成品
——封装了特定处理流程和控制逻辑
——成熟的,不断升级改进的软件
框架和类库的区别
——框架一般是封装了逻辑,高内聚的,类库则是松散的工具组合
——框架专注于某以领域,类库则是更通用的
为什么使用框架
*软件日趋复杂
*重用度高,开发效率和质量提高
*软件设计人员要专注于对领域的了解,是需求更加充分
*易于上手,快速解决问题
IOC
接口
*用于沟通的中介物的抽象化
*实体把自己提供给外界的一种抽象化说明,用以有内部操作分离出外部沟通方法,使其能被修改内部而不影响外界其他实体与其交互的方式
*对应Java接口即声明,声明了哪些方法是对外公开提供的
*在Java8中,接口可以拥有方法体
面向接口编程
IOC控制反转:哪些方面的控制被反转了呢?
获取依赖对象的过程被反转了,控制被反转之后,获得依赖的对象的过程被反转了,获取依赖的对象有自身管理变成了IOC容器主动注入,于是,他给控制反转去了更合适的名字“依赖”注入
IOC中所有的对象都叫做bean
bean容器初始化
Spring注入
*Spring注入是指在启动Spring容器加载bean配置的时候,完成对变量的赋值行为
*常用的两种注入方式
——设值注入
——构造注入
DAO:对于数据库的访问
Service:处理业务逻辑的部分
Bean
*Bean的配置项
id:配置项中的唯一标识
Class:具体要实例化的一个类(必须的)
Scope:范围,作用域
Constructor arguments:构造器的参数
Properties:属性
Autowiring mode:自动装配的模式
lazy-initialization mode:懒加载模式
Intialization/desturction method:初始化和销毁的方法
*Bean的作用域:
*Bean的生命周期:
- 定义:在Bean的配置文件(xml)中配置的Bean,Bean的id以及Bean的Class
- 初始化:加载并配置文件Bean,并初始化生成Bean的实例
- 使用:在单元测试中或者实际使用中从Bean中取出一个Bean的实例,去调用他的方法
- 销毁:在baen停止的时候,销毁这个Bean产生的实例
*Bean的自动装配
*Resources&ResourcesLoader
Aware
*Spring中提供了一些以Aware结尾的接口,实现了Aware接口的bean在被初始化之后,可以获得相应的资源
*通过Aware接口,可以对Spring相应资源进行操作(一定要慎重)
*为对Spring进行简单的扩展提供了方便的入口
----------------------------------------------------------------------------------------------------------------------------------------------------Spring学习笔记
IOC
控制反转,将原本程序中手动创建UserService对象的控制权,交由Spring框架管理
简单说,就是把创建UserService对象控制权被反转到了Spring框架
DI 依赖注入
就是在Spring创建这个对象的过程中,将这个对象所依赖的属性注入进去
Bean标签的一些常用的属性
id和name
一般情况下,装配一个bean的时候,通过指定一个id属性作为Bean的名称
id属性在ioc容器中必须的唯一的
如果bean的名称中有特殊字符,就需要使用name属性
class
class用于设置一个类的全路径,用Spring来生成这个类的实例和反射
由scope配置
singleton是默认值,单例
prototype,每一次都会创建一个新的实例,也就是说是多例的
Spring的注解方式注入
@Component、@Repository、@Service、@Comtroller等都是相当于在配置文件中对
@Component("userService")//这就相当于是
如果是普通类型用@Value,这个注解给key设值
/*
正常这时候得需要一个set方法
然后在配置文件中使用
*/
@Value("米饭")
privateStringsomething;
/*
如果提供了set方法,这时候注解需要添加在set方法前
*/
@Value("米饭")
publicvoidsetSomething(Stringsomething) {
this.something=something;
}
如果是对象类型的话用一下方法,例如:UserDao的注解
@Autowired
@Qualifier("userDao")
privateUserDaouserDao;
@Repository("userDao")
publicclassUserDao{
publicvoidsave(){
System.out.println("Dao层保存数据");
}
}
//其中的@Qualifier("userDao")和@Repository("userDao")中的userDao名称一定要一致,否则会报错
//
@Resource(name="userDao")就相当于@Autowired@Qualifier("userDao")//Resource也是强制用名称注入
Spring的其他注解
@Component("bean1")
publicclassBean1{
@PostConstruct
publicvoidinit(){
System.out.println("initBean......");
}
publicvoidsay(){
System.out.println("sayBean.......");
}
@PreDestroy
publicvoiddestory(){
System.out.println("destoryBean..........");
}
=======================================================================================
@Component("bean2")
@Scope("prototype")
publicclassBean2{
}
======================================================================================
@Test
publicvoiddemo3(){
ClassPathXmlApplicationContextapplicationContext=newClassPathXmlApplicationContext("applicationContext.xml");
Demo2bean1=(Demo2)applicationContext.getBean("demo2");
Demo2bean2=(Demo2)applicationContext.getBean("demo2");
System.out.println(bean1==bean2);
}
===================================================================================
当bean2的Scope为默认值singleton的为单例的 ,bean1 == bean2的值为true;
当bean2的Scope为prototype的为多例的 ,bean1 == bean2的值为false;
传统xml配置与注解配置混合使用
publicclassProduceService{
@Resource(name="categoryDao")
privateCategoryDaocategoryDao;
@Resource(name="productDao")
privateProductDaoproductDao;
// public void setCategoryDao(CategoryDao categoryDao) {
// this.categoryDao = categoryDao;
// }
//
// public void setProductDao(ProductDao productDao) {
// this.productDao = productDao;
// }
publicvoidsave(){
System.out.println("ProduceServicede的save()方法被执行了");
categoryDao.save();
productDao.save();
}
}
=========================================================================================
正常是通过set方法
publicvoidsetCategoryDao(CategoryDaocategoryDao) {
// this.categoryDao = categoryDao;
// }
//
// public void setProductDao(ProductDao productDao) {
// this.productDao = productDao;
// }
然后使用xml配置文件配置
启用
@Resource(name="categoryDao")
privateCategoryDaocategoryDao;
@Resource(name="productDao")
privateProductDaoproductDao;
Spring AOP
publicclassUserDaoImplimplementsUserDao{
publicvoidsave(Useruser){
publicvoidcheckPrivilege(){
checkPrivilege()
...保存用户
}
publicvoidupdate(Useruser){
...更新用户
}
publicvoiddelete(Useruser){
...删除用户
}
publicListfind( ){
...查询用户
}
publicvoidcheckPrivilege(){
//检查权限
}
}
这个类中保存用户时,调用checkPrivilege()方法检查权限。但是这只是一个类,如果有很多个类,要再每一个来中都写这么一个方法然后调用时非常麻烦的
传统方法:纵向继承
publicclassBaseDaoImpl{
publicvoidcheckPrivilege(){
}
}
publicclassUserDaoImplextendsBaseDaoImpl{
publicvoidsave(Useruser){
checkPrivilege()
//保存用户
}
}
没有使用AOP的方式:
定义一个父类的实现类,父类中有一个检查权限的方法
让其他的类继承这个父类的实现类,子类中可以有父类中的方法,直接调用checkPrivilege()检查权限
使用AOP的动态代理方法:采用横向抽取机制
SpringAOP使用纯java实现,不需要专门的编译过程和类加载器,在运行期通过代理方式想目标类织入增强代码
AOP的相关术语:
publicclassUserDaoImplimplementsUserDao{
publicvoidsave(Useruser){
publicvoidcheckPrivilege(){
checkPrivilege()
...保存用户
}
publicvoidupdate(Useruser){
...更新用户
}
publicvoiddelete(Useruser){
...删除用户
}
publicListfind( ){
...查询用户
}
publicvoidcheckPrivilege(){
//检查权限
}
}
Joinpoint(连接点):指的是可以拦截到的点
增删改查这些方法都可以被增强,这些方法成为是连接点
Pointcut(切入点):指的是真正被拦截到的点
指想对save()方法进行增强(做权限校验),save方法称为是切入点
Advice(通知):拦截后要做的事:
对save()方法进行权限校验,权限校验的方法称之为通知
通知有前置和后置以及环绕通知
前置通知:比如说想在save方法之前做权限校验,这就叫前置通知
后置通知:比如说要在delete方法做一个日志记录,这就叫后置通知
环绕通知:在之前和之后都会做一些事情
Target(目标):被增强的对象,例如:UserDaoImpl
Weaving(织入):将Advice应用到Target的过程
将权限校验应用到UserDaoImpl的save()方法中的整个过程
Proxy(代理对象):被应用了增强后,产生的一个代理对象
Aspect(切面):就是切入点和通知的组合
JDK的动态代理
定义一个接口
publicinterfaceUserDao{
publicvoidsave();
publicvoiddelete();
publicvoidupdate();
publicvoidfind();
}
接口的实现类
publicclassUserDaoImplimplementsUserDao{
publicvoidsave() {
System.out.println("保存用户");
}
publicvoiddelete() {
System.out.println("删除用户");
}
publicvoidupdate() {
System.out.println("修改用户");
}
publicvoidfind() {
System.out.println("查询用户");
}
}
编写一个动态代理类
/*
这个构造方法的作用是获取到需要增强的对象
*/
publicMyjdkProxy(UserDaouserDao){
this.userDao=userDao;
}
=====================================================================================
publicObjectcreateProxy(){//用createProxy()这个方法产生一个代理,首选jdk的动态代理,因为jdk的动态代理可以对实现了接口的类实现代理
/*
传入的参数:类加载器userDao.getClass().getClassLoader();实现的接口userDao.getClass().getInterfaces();第三个参数是一个接口InvocationHandler
*/
Objectproxy=Proxy.newProxyInstance(userDao.getClass().getClassLoader(),userDao.getClass().getInterfaces(),this);
returnproxy;
}
publicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{
if("save".equals(method.getName())){
System.out.println("权限校验");
returnmethod.invoke(userDao,args);
}
returnmethod.invoke(userDao,args);
}
====================================================================================
/*
编写测试类,使用代理
*/
UserDaouserDao=newUserDaoImpl();
UserDaoproxy=(UserDao)newMyjdkProxy(userDao).createProxy();
proxy.save();
proxy.delete();
proxy.update();
proxy.find();
}
CGLIB动态代理
底层:生成了一个类来继承目标类
对于不使用接口的业务层,无法使用jdk的动态代理
CGLIB采用非常底层字节码技术,可以为一个类创建字类,解决无接口代理问题
publicObjectcreateProxy(){
//1.创建一个CGLIB的核心类
Enhancerenhancer=newEnhancer();
//2.设置父类
enhancer.setSuperclass(object.getClass());
//3.设置回调
enhancer.setCallback(this);
//4.生成代理
Objectproxy=enhancer.create;
returnproxy;
}
代理知识总结:
Spring在运行期,生成动态代理对象,不需要特殊的编译器
Spring AOP的底层就是通过jdk的动态代理或者CGLIB动态代理技术,为目标Bean执行横向织入
若目标对象实现了若干接口,spring使用jdk的动态代理:java.lang.reflect.Proxy类代理
若目标对象没事实现任何的接口,spring使用CGLIB库生成目标对象的子类
程序中应优先对接口创建代理,以便于程序的解耦合以及后期的维护
标记为final的方法,不能被代理,因为无法进行覆盖重写:因为她没有办法去重写父类或者父接口上面的方法
JDK的动态代理,是针对接口生成子类,接口中的方法不能使用final修饰
CGLIB是针对目标类生成子类,因此类或方法不能使用final
Spring只支持方法连接点,不提供属性连接点
一般切面的代码案例
定义StudentDao接口
publicinterfaceStudentDao{
publicvoidsave();
publicvoidfind();
publicvoidupdate();
publicvoiddelete();
}
接口的实现类,实现了接口StudentDao
publicclassStudentDaoImplimplementsStudentDao{
publicvoidsave() {
System.out.println("save执行了。。。。。。。");
}
publicvoidfind() {
System.out.println("find执行了。。。。。。。");
}
publicvoidupdate() {
System.out.println("update执行了。。。。。。");
}
publicvoiddelete() {
System.out.println("delete执行了。。。。。。");
}
}
定义一个一般切面Advisor
publicclassMyBeforeAdviceimplementsMethodBeforeAdvice{
publicvoidbefore(Methodmethod,Object[]objects,Objecto)throwsThrowable{
System.out.println("前置通知======================");
}
}
配置文件中配置
Spring带有切入点的切面配置
我们需要代理的对象
publicclassCustomerDao{
publicvoidfind(){
System.out.println("查询用户...");
}
publicvoidsave(){
System.out.println("保存用户...");
}
publicvoidupdate(){
System.out.println("修改用户");
}
publicvoiddelete(){
System.out.println("删除用户");
}
通知Advice
/*
环绕通知时最强大的一种通知类型,它可以阻止目标方法的执行
*/
publicclassMyArroundAdviceimplementsMethodInterceptor{
publicObjectinvoke(MethodInvocationinvocation)throwsThrowable{
System.out.println("环绕前增强...");
Objectobj=invocation.proceed();//用来执行目标方法,比如说原来的方法是执行保存,这句哈的意思就相当于执行保存的操作
System.out.println("环绕后增强...");
returnobj;
}
配置
自动创建代理
BeanNameAutoProxyCreate举例
基于Bean名称的自动代理的创建方式,根据目标类的名称产生代理
先把前几个例子中的接口,接口实现类,通知等拷贝过来
publicclassCustomerDao{
publicvoidfind(){
System.out.println("查询用户...");
}
publicvoidsave(){
System.out.println("保存用户...");
}
publicvoidupdate(){
System.out.println("修改用户");
}
publicvoiddelete(){
System.out.println("删除用户");
}
}
publicinterfaceStudentDao{
publicvoidsave();
publicvoidfind();
publicvoidupdate();
publicvoiddelete();
}
publicclassStudentDaoImplimplementsStudentDao{
publicvoidsave() {
System.out.println("save执行了。。。。。。。");
}
publicvoidfind() {
System.out.println("find执行了。。。。。。。");
}
publicvoidupdate() {
System.out.println("update执行了。。。。。。");
}
publicvoiddelete() {
System.out.println("delete执行了。。。。。。");
}
}
publicclassMyBeforeAdviceimplementsMethodBeforeAdvice{
publicvoidbefore(Methodmethod,Object[]objects,Objecto)throwsThrowable{
System.out.println("前置通知======================");
}
}
publicclassMyArroundAdviceimplementsMethodInterceptor{
publicObjectinvoke(MethodInvocationinvocation)throwsThrowable{
System.out.println("环绕前增强...");
Objectobj=invocation.proceed();//用来执行目标方法,比如说原来的方法是执行保存,这句哈的意思就相当于执行保存的操作
System.out.println("环绕后增强...");
returnobj;
}
}
配置文件配置
这种自动代理有缺陷,比如说只想增强其中的save方法该怎么做到呢,这是基于Bean名称的方式又不太合适了
DefalutAdvisorAutoProxyCreator举例
默认的切面的自动代理的方式创建,这种方式可以基于切面的信息自动产生代理
前几个步骤与上一个方法类似,只是在配置文件中不同
配置了一个切面,因为是基于切面的信息自动产生代理
在切面配置中,
AspectJ的注解AOP开发
AspectJ简介
AspectJ是一个基于Java语言的AOP框架
Spring2.0以后新增了对AspectJ切点表达式的支持
@AspectJ是AspectJ1.5新增的功能,通过JDK5注解技术,允许直接在Bean类中定义切面
新版本的Spring框架,都建议使用AspectJ方式来开发AOP
need-to-insert-img
为目标类,定义切面类
publicclassPeoductDao{
publicvoidsave(){
System.out.println("保存商品");
}
publicvoiddelete(){
System.out.println("删除商品");
}
publicvoidupdate(){
System.out.println("修改商品");
}
publicvoidfindOne(){
System.out.println("查询一个商品");
}
publicvoidfindAll(){
System.out.println("查询所有的商品");
}
/*
切面类
*/
@Aspect
publicclassMyAspect{
@Before(value="execution(* com.imooc.aspectJ.demo1.PeoductDao.save(..))")
publicvoidbefore(){
System.out.println("前置通知");
}
详细介绍Before前置通知
need-to-insert-img
@AfterReturing 后置通知
@AfterReturning(value="execution(* com.imooc.aspectJ.demo1.PeoductDao.update(..))",returning="re")
publicvoidafterReturing(Objectre){//为什么要用Object,因为这个返回值有可能是任意类型的返回值,所以用Object;
System.out.println("后置通知============="+re);
}
//为什么要用Object,因为这个返回值有可能是任意类型的返回值,所以用Object;
@AfterReturing 异常抛出通知
@Around环绕通知
need-to-insert-img
重点:如果不调用joinPoint.proceed();方法的话,目标方法会被拦截,即delete不会执行
通过@Pointcut为切点命名
在每个通知内定义切点,会造成工作量大,不易维护,对于重复的切点,可以使用@Pointcut进行定义
切点方法:private void 无参数方法。方法名为切点名;因为这个方法在类中没有什么实际的意义,也不用在别的方法中调用它,所以定义为private
当通知多个切点时,可以使用||进行连接
基于AspectJ的xml方式的AOP开发
通过配置的方式完成AOP的开发
publicinterfaceCustomerDao{
publicvoidsave();
publicvoidfindOne();
publicvoidfingAll();
publicvoidupdate();
publicvoiddelete();
}
publicclassCustomerDaoImplimplementsCustomerDao{
publicvoidsave() {
System.out.println("保存客户");
}
publicvoidfindOne() {
System.out.println("查找一个客户");
}
publicvoidfingAll() {
System.out.println("查找所有的客户");
}
publicvoidupdate() {
System.out.println("修改客户");
}
publicvoiddelete() {
System.out.println("删除客户");
}
need-to-insert-img
need-to-insert-img
need-to-insert-img
JDBC Template