Spring框架

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都会调用初始化和销毁的方法   

*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等都是相当于在配置文件中对中的id的配置

@Component("userService")//这就相当于是的id;

如果是普通类型用@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配置文件配置

启用后,可以通过注解方式来代替先创建set方法然后在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

你可能感兴趣的:(Spring框架)