Spring:春天--->给软件行业带来了春天
2002年,RodJahnson首次推出了Spring框架雏形interface21框架。
2004年3月24日,Spring框架以interface21框架为基础,经过重新设计,发布了1.0正式版。
很难想象RodJohnson的学历,他是悉尼大学的博士,然而他的专业不是计算机,而是音乐学。
Spring理念:使现有技术更加实用.本身就是一个大杂烩,整合现有的框架技术
Spring是一个开源免费的框架,容器.
Spring是一个轻量级的框架,非侵入式的.
控制反转IoC,面向切面Aop
对事物的支持,对框架的支持
一句话概括:Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器(框架)。
Spring框架是一个分层架构,由7个定义良好的模块组成。Spring模块构建在核心容器之上,核心容器定义了创建、配置和管理bean的方式.
组成Spring框架的每个模块(或组件)都可以单独存在,或者与其他一个或多个模块联合实现。每个模块的功能如下:
SpringBoot与SpringCloud
新建一个空白的maven项目
我们先用我们原来的方式写一段代码.
先写一个UserDao接口
publicinterfaceUserDao{publicvoidgetUser();}再去写Dao的实现类
publicclassUserDaoImplimplementsUserDao{ @Override publicvoidgetUser(){ System.out.println("获取用户数据"); }}然后去写UserService的业务接口
publicinterfaceUserService{ publicvoidgetUser();}最后写Service的实现类
publicclassUserServiceImplimplementsUserService{ privateUserDaouserDao=newUserDaoImpl(); @Override publicvoidgetUser(){ userDao.getUser(); }}测试一下
@Testpublicvoidtest(){//用户实际调用的是业务层,dao层他们不需要按触! UserServiceservice=newUserServiceImpl(); service.getUser();}这是我们原来的方式,开始大家也都是这么去写的对吧.那我们现在修改一下.把Userdao的实现类增加一个.
publicclassUserDaoMySqlImplimplementsUserDao{@OverridepublicvoidgetUser(){System.out.println("MySql获取用户数据");}}紧接着我们要去使用MySql的话,我们就需要去service实现类里面修改对应的实现
publicclassUserServiceImplimplementsUserService{privateUserDaouserDao=newUserDaoMySqlImpl();//这里@OverridepublicvoidgetUser(){userDao.getUser();}}在假设,我们再增加一个Userdao的实现类.
publicclassUserDaoOracleImplimplementsUserDao{@OverridepublicvoidgetUser(){System.out.println("Oracle获取用户数据");}}那么我们要使用Oracle,又需要去service实现类里面修改对应的实现。假设我们的这种需求非常大,这种方式就根本不适用了,甚至反人类对吧,每次变动,都需要修改大量代码。这种设计的耦合性太高了,牵一发而动全身。
那我们如何去解决呢
我们可以在需要用到他的地方,不去实现它,而是留出一个接口,利用set,我们去代码里修改下。
publicclassUserServiceImplimplementsUserService{privateUserDaouserDao;//利用set函数进行动态值实现注入publicvoidsetUserDao(UserDaouserDao){this.userDao=userDao;}@OverridepublicvoidgetUser(){userDao.getUser();}}现在去我们的测试类里,进行测试;
@Testpublicvoidtest(){UserServiceImplservice=newUserServiceImpl();service.setUserDao(newUserDaoMySqlImpl());service.getUser();//那我们现在又想用Oracle去实现呢service.setUserDao(newUserDaoOracleImpl());service.getUser();}已经发生了根本性的变化,很多地方都不一样了。以前所有东西都是由程序去进行控制创建,而现在是由我们自行控制创建对象,把主动权交给了调用者.程序不用去管怎么创建,怎么实现了。它只负责提供一个接口。
控制反转IoC(InversionofControl),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,也有人认为DI只是IoC的另一种说法。没有IoC的程序中,我们使用面向对象编程,对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了。
IoC是Spring框架的核心内容,使用多种方式完美的实现了IoC,可以使用XML配置,也可以使用注解,新版本的Spring也可以零配置实现IoC。
Spring容器在初始化时先读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,程序使用时再从Ioc容器中取出需要的对象。
采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。
控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(DependencyInjection,DI)。
、导入Jar包
注:spring需要导入commons-logging进行日志记录.我们利用maven,它会自动下载对应的依赖项。
导入spring的jar包
publicclassHello{privateStringname;publicStringgetName(){returnname;}publicvoidsetName(Stringname){this.name=name;}publicvoidshow(){System.out.println("Hello,"+name);}}编写我们的spring文件,这里我们命名为beans.xml
@Testpublicvoidtest(){//解析beans.xml文件,生成管理相应的Bean对象//获取Spring的上下文对象ApplicationContextcontext=newClassPathXmlApplicationContext("beans.xml");//getBean:参数即为spring配置文件中bean的id。//我们的对象都在spring管理了,我们要使用,直接去里面取出来就可以Hellohello=(Hello)context.getBean("hello");hello.show();}思考Hello对象是谁创建的【hello对象是由Spring创建的】
Hello对象的属性是怎么设置的【hello对象的属性是由Spring容器设置的】这个过程就叫控制反转:
控制:谁来控制对象的创建,传统应用程序的对象是由程序本身控制创建的,使用Spring后,对象是由Spring来创建的
反转:程序本身不创建对象,而变成被动的接收对象.
依赖注入:就是利用set方法来进行注入的.
IOC是一种编程思想,由主动的编程变成被动的接收
可以通过newClassPathXmlApplicationContext去浏览一下底层源码.
我们在案例一中,新增一个Spring配置文件beans.xml
@Testpublicvoidtest2(){//获取ApplicationContext;拿到Spring的容器ApplicationContextcontext=newClassPathXmlApplicationContext("beans.xml");UserServiceImplserviceImpl=(UserServiceImpl)context.getBean("ServiceImpl");serviceImpl.getUser();}OK,到了现在,我们彻底不用再程序中去改动了,要实现不同的操作,只需要在xml配置文件中进行修改,所谓的IoC,一句话搞定:对象由Spring来创建,管理,装配!
User.java
publicclassUser{privateStringname;publicUser(){System.out.println("user的无参构造函数");}publicStringgetName(){returnname;}publicvoidsetName(Stringname){this.name=name;}publicvoidshow(){System.out.println("User{name="+name+"}");}}beans.xml
@Testpublicvoidtest3(){ApplicationContextcontext=newClassPathXmlApplicationContext("beans.xml");Useruser=(User)context.getBean("User");System.out.println(user);}结果可以发现,在调用show方法之前,User对象已经通过无参构造初始化了!
publicclassUser{privateStringname;publicUser(){System.out.println("user的无参构造函数");}publicUser(Stringname){this.name=name;}publicStringgetName(){returnname;}publicvoidsetName(Stringname){this.name=name;}publicvoidshow(){System.out.println("User{name="+name+"}");}}beans.xml有三种方式编写
@Testpublicvoidtest4(){ApplicationContextcontext=newClassPathXmlApplicationContext("beans.xml");Useruser=(User)context.getBean("user");user.show();}结论:在配置文件加载的时候。其中管理的对象(所有bean)都已经初始化了!
别名
alias设置别名,为bean设置别名,可以设置多个别名
团队的合作通过import来实现,相当于合并到导入的文件
可以将多个配置文件,导入合并为一个。假设,现在项目中有多个人开发,这三个人复制不同的类开发,不同的类需要注册在不同的bean中,我们可以利用import将所有人的beans.xml合并为一个总的。
applicationContext.xml
依赖:指Bean对象的创建依赖于容器.Bean对象的依赖资源.
注入:指Bean对象所依赖的资源,由容器来设置和装配.
在之前的案例已经详细讲过了
要求被注入的属性,必须有set方法,set方法的方法名由set+属性首字母大写,如果属性是boolean类型,没有set方法,是is.
测试pojo类:
Address.java
publicclassAddress{privateStringaddress;publicStringgetAddress(){returnaddress;}publicvoidsetAddress(Stringaddress){this.address=address;}}Student.java
@Testpublicvoidtest01(){ApplicationContextcontext=newClassPathXmlApplicationContext("applicationContext.xml");Studentstudent=(Student)context.getBean("student");System.out.println(student.getName());}Bean注入注意点:这里的值是一个引用,ref
5、Map注入
p命名和c命名注入
User.java:【注意:这里没有有参构造器!】
publicclassUser{privateStringname;privateintage;publicvoidsetName(Stringname){this.name=name;}publicvoidsetAge(intage){this.age=age;}@OverridepublicStringtoString(){return"User{"+"name='"+name+'\''+",age="+age+'}';}}1、P命名空间注入:需要在头文件中加入约束文件
解决:把有参构造器加上,这里也能知道,c就是所谓的构造器注入!为了防止p命名注入错误,也需要把无参构造加上。
测试代码:
@Testpublicvoidtest02(){ApplicationContextcontext=newClassPathXmlApplicationContext("applicationContext.xml");Useruser=(User)context.getBean("user");System.out.println(user);}Bean的作用域在Spring中,那些组成应用程序的主体及由SpringIoC容器所管理的对象,被称之为bean。简单地讲,bean就是由IoC容器初始化、装配及管理的对象.
几种作用域中,request、session作用域仅在基于web的应用中使用(不必关心你所采用的是什么web应用框架),只能用在基于web的SpringApplicationContext环境。
单例模式(Spring默认机制)
当一个bean的作用域为Singleton,那么SpringIoC容器中只会存在一个共享的bean实例,并且所有对bean的请求,只要id与该bean定义相匹配,则只会返回bean的同一实例。Singleton是单例类型,就是在创建起容器时就同时自动创建了一个bean的对象,不管你是否使用,他都存在了,每次获取到的对象都是同一个对象。注意,Singleton作用域是Spring中的缺省作用域。要在XML中将bean定义成singleton,可以这样配置:
@Testpublicvoidtest03(){ApplicationContextcontext=newClassPathXmlApplicationContext("applicationContext.xml");Useruser=(User)context.getBean("user");Useruser2=(User)context.getBean("user");System.out.println(user==user2);}Prototype原型模式:每次从容器中get的时候,都会产生一个新对象
当一个bean的作用域为Prototype,表示一个bean定义对应多个对象实例。Prototype作用域的bean会导致在每次对该bean请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean()方法)时都会创建一个新的bean实例。Prototype是原型类型,它在我们创建容器的时候并没有实例化,而是当我们获取bean的时候才会去创建一个对象,而且我们每次获取到的对象都不是同一个对象。根据经验,对有状态的bean应该使用prototype作用域,而对无状态的bean则应该使用singleton作用域。在XML中将bean定义成prototype,可以这样配置:
当一个bean的作用域为Session,表示在一个HTTPSession中,一个bean定义对应一个实例。该作用域仅在基于web的SpringApplicationContext情形下有效。考虑下面bean定义:
在Spring中有三种装配的方式
这里我们主要讲第三种:自动化的装配bean。
Spring的自动装配需要从两个角度来实现,或者说是两个操作:
组件扫描和自动装配组合发挥巨大威力,使得显示的配置降低到最少。
推荐不使用自动装配xml配置,而使用注解.
测试环境搭建
1、新建一个项目
2、新建两个实体类,CatDog都有一个叫的方法
publicclassCat{publicvoidshout(){System.out.println("miao~");}}publicclassDog{publicvoidshout(){System.out.println("wang~");}}3、新建一个用户类User
publicclassPeople{privateCatcat;privateDogdog;privateStringstr;//set与get}4、编写Spring配置文件
publicclassMyTest{@Testpublicvoidtest1(){ApplicationContextcontext=newClassPathXmlApplicationContext("beans.xml");Peoplepeople=context.getBean("people",People.class);people.getCat().shout();people.getDog().shout();}}结果正常输出,环境OK
环境搭建:一个人有两个宠物
jdk1.5支持的注解,Spring2.5就支持注解了!
要使用注解须知:
使用Autowired我们可以不用编写set方法了,前提是你这个自动装配的属性在IOC(Spring)容器中存在,且符合名字byName
publicclassPeople{@AutowiredprivateCatcat;@AutowiredprivateDogdog;privateStringstr;publicCatgetCat(){returncat;}publicvoidsetCat(Catcat){this.cat=cat;}publicDoggetDog(){returndog;}}科普:
@Nullable 字段标记了这个注解,说明这个字段可以为null;public@interfaceAutowired{booleanrequired()defaulttrue;}测试代码:
publicclassPeople{//如果显示定义了Autowired的required属性为false,说明这个对象可以为Null,否则不允许为空@Autowired(required=false)privateCatcat;@AutowiredprivateDogdog;privateStringname;}如果@Autowired自动装配的环境比较复杂,自动装配无法通过一个注解【@Autowired】完成的时候,我们可以使用@Qualifier(value=“xxx”)去配合@Autowire的使用,指定一个唯一的bean对象注入!
publicclassPeople{@Autowired@Qualifier(value="cat2")privateCatcat;@AutowiredprivateDogdog;privateStringname;}@Resource@Resource如有指定的name属性,先按该属性进行byName方式查找装配;其次再进行默认的byName方式进行装配;如果以上都不成功,则按byType的方式自动装配。都不成功,则报异常。结论:先进行byName查找,失败;再进行byType查找,成功。
publicclassPeople{@Resource(name="cat3")privateCatcat;@ResourceprivateDogdog;privateStringname;}小结:
@Autowired与@Resource异同:
1、@Autowired与@Resource都可以用来装配bean。都可以写在字段上,或写在setter方法上。
2、@Autowired默认按类型装配(属于spring规范),默认情况下必须要求依赖对象必须存在,如果要允许null值,可以设置它的required属性为false,如:@Autowired(required=false),如果我们想使用名称装配可以结合@Qualifier注解进行使用
3、@Resource(属于J2EE复返),默认按照名称进行装配,名称可以通过name属性进行指定。如果没有指定name属性,当注解写在字段上时,默认取字段名进行按照名称查找,如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。
它们的作用相同都是用注解方式注入对象,但执行顺序不同。@Autowired先byType,@Resource先byName。
说明
在spring4之后,想要使用注解形式,必须得要引入aop的包
在配置文件当中,还得要引入一个context约束
我们之前都是使用bean的标签进行bean注入,但是实际开发中,我们一般都会使用注解!
1、配置扫描哪些包下的注解
@Component("user")//相当于配置文件中
@Testpublicvoidtest(){ApplicationContextapplicationContext=newClassPathXmlApplicationContext("beans.xml");Useruser=(User)applicationContext.getBean("user");System.out.println(user.name);}属性注入
使用注解注入属性
1、可以不用提供set方法,直接在直接名上添加@value("值")
@Component("user")//相当于配置文件中
@Component("user")publicclassUser{publicStringname;@Value("sdz")publicvoidsetName(Stringname){this.name=name;}}衍生注解
我们这些注解,就是替代了在配置文件当中配置步骤而已!更加的方便快捷!
@Component三个衍生注解
为了更好的进行分层,Spring可以使用其它三个注解,功能一样,目前使用哪一个功能都一样。
写上这些注解,就相当于将这个类交给Spring管理装配了!
这四个注解功能都是一样的,都是代表将某个类注册到Spring中,装配Bean
自动装配注解
在Bean的自动装配。
-@Autowired:自动装配通过类型。名字如果Autowired不能唯-。-自动装配上属性,则需要通过@Qualifier(value=“xxx”)-@Nullable字段标记了这个注解,说明这个字段可以为null;-@Resource:自动装配通过名字。类型作用域
@scope
@Controller("user")@Scope("prototype")publicclassUser{@Value("sdz")publicStringname;}小结
XML与注解比较
xml与注解整合开发:推荐最佳实践
进行注解驱动注册,从而使注解生效
用于激活那些已经在spring容器里注册过的bean上面的注解,也就是显示的向Spring注册
如果不扫描包,就需要手动配置bean
如果不加注解驱动,则注入的值为null!
JavaConfig原来是Spring的一个子项目,它通过Java类的方式提供Bean的定义信息,在Spring4的版本,JavaConfig已正式成为Spring4的核心功能。
测试:
1、编写一个实体类,Dog
//这里这个注解的意思,就是说明这个类被Spring接管了,注册到人气中@ComponentpublicclassUser{privateStringname;publicStringgetName(){returnname;}@Value("sdz")//注册值publicvoidsetName(Stringname){this.name=name;}@OverridepublicStringtoString(){return"User{"+"name='"+name+'\''+'}';}}2、新建一个config配置包,编写一个SdzConfig配置类
@Configuration//这个也会被spring容器托管,注册到容器中。//因为它本来就是一个@Component,@Configuration代表这是一个配置类,等价于beans.xml@ComponentScan("com.sdz")//扫描包publicclassSdzConfig{//注册一个Bean,相当于我们之前写的一个bean标签//这个方法的名字,就相当于bean标签中的id属性//这个方法的返回值,就相当于bean标签中的class属性@BeanpublicUsergetUser(){returnnewUser();//就是返回要注入bean的对象}}3、测试
publicclassMyTest{@Testpublicvoidtest1(){//如果完全使用了配置类方式去做,我们就只能通过AnnotationConfig上下文来获收容器,通过配置类的cLass对象加载!ApplicationContextcontext=newAnnotationConfigApplicationContext(SdzConfig.class);UsergetUser=(User)context.getBean("getUser");System.out.println(getUser.getName());}}4、成功输出结果!
导入其他配置如何做呢?
1、我们再编写一个配置类!
@Configuration//代表这是一个配置类publicclassMyConfig2{}2、在之前的配置类中我们来选择导入这个配置类
@Configuration@Import(MyConfig2.class)//导入合并其他配置类,类似于配置文件中的inculde标签publicclassMyConfig{@BeanpublicDogdog(){returnnewDog();}}关于这种Java类的配置方式,我们在之后的SpringBoot和SpringCloud中还会大量看到,我们需要知道这些注解的作用即可!
为什么要学习代理模式,因为AOP的底层机制就是动态代理!
代理模式:
学习aop之前,我们要先了解一下代理模式!
静态代理角色分析
抽象角色:一般使用接口或者抽象类来实现
真实角色:被代理的角色
代理角色:代理真实角色;代理真实角色后,一般会做一些附属的操作.
客户:使用代理角色来进行一些操作.
代码实现
Rent.java即抽象角色
//抽象角色:租房publicinterfaceRent{publicvoidrent();}Host.java即真实角色
//真实角色:房东,房东要出租房子publicclassHostimplementsRent{publicvoidrent(){System.out.println("房屋出租");}}Proxy.java即代理角色
//代理角色:中介publicclassProxyimplementsRent{privateHosthost;publicProxy(){}publicProxy(Hosthost){this.host=host;}//租房publicvoidrent(){seeHouse();host.rent();fare();}//看房publicvoidseeHouse(){System.out.println("带房客看房");}//收中介费publicvoidfare(){System.out.println("收中介费");}}Client.java即客户
//客户类,一般客户都会去找代理!publicclassClient{publicstaticvoidmain(String[]args){//房东要租房Hosthost=newHost();//中介帮助房东租房Proxyproxy=newProxy(host);//你去找中介!proxy.rent();}}分析:在这个过程中,你直接接触的就是中介,就如同现实生活中的样子,你看不到房东,但是你依旧租到了房东的房子通过代理,这就是所谓的代理模式,程序源自于生活,所以学编程的人,一般能够更加抽象的看待生活中发生的事情。
静态代理的好处:
缺点:
我们想要静态代理的好处,又不想要静态代理的缺点,所以,就有了动态代理!
练习步骤:
1、创建一个抽象角色,比如咋们平时做的用户业务,抽象起来就是增删改查!
//抽象角色:增删改查业务publicinterfaceUserService{voidadd();voiddelete();voidupdate();voidquery();}2、我们需要一个真实对象来完成这些增删改查操作
//真实对象,完成增删改查操作的人publicclassUserServiceImplimplementsUserService{publicvoidadd(){System.out.println("增加了一个用户");}publicvoiddelete(){System.out.println("删除了一个用户");}publicvoidupdate(){System.out.println("更新了一个用户");}publicvoidquery(){System.out.println("查询了一个用户");}}3、需求来了,现在我们需要增加一个日志功能,怎么实现!
4、设置一个代理类来处理日志!代理角色
//代理角色,在这里面增加日志的实现publicclassUserServiceProxyimplementsUserService{privateUserServiceImpluserService;publicvoidsetUserService(UserServiceImpluserService){this.userService=userService;}publicvoidadd(){log("add");userService.add();}publicvoiddelete(){log("delete");userService.delete();}publicvoidupdate(){log("update");userService.update();}publicvoidquery(){log("query");userService.query();}publicvoidlog(Stringmsg){System.out.println("执行了"+msg+"方法");}}5、测试访问类:
publicclassClient{publicstaticvoidmain(String[]args){//真实业务UserServiceImpluserService=newUserServiceImpl();//代理类UserServiceProxyproxy=newUserServiceProxy();//使用代理类实现日志功能!proxy.setUserService(userService);proxy.add();}}OK,到了现在代理模式大家应该都没有什么问题了,重点大家需要理解其中的思想;
我们在不改变原来的代码的情况下,实现了对原有功能的增强,这是AOP中最核心的思想
聊聊AOP:纵向开发,横向开发
动态代理的角色和静态代理的一样.
动态代理的代理类是动态生成的。静态代理的代理类是我们提前写好的
动态代理分为两类:一类是基于接口动态代理,一类是基于类的动态代理
JDK的动态代理需要了解两个类
核心:InvocationHandler和Proxy,打开JDK帮助文档看看
【InvocationHandler:是由代理实例的调用处理程序实现的接口。】
//生成代理类publicObjectgetProxy(){returnProxy.newProxyInstance(this.getClass().getClassLoader(),rent.getClass().getInterfaces(),this);}代码实现
抽象角色和真实角色和之前的一样!
//真实角色:房东,房东要出租房子publicclassHostimplementsRent{publicvoidrent(){System.out.println("房屋出租");}}ProxyInvocationHandler.java即代理角色
publicclassProxyInvocationHandlerimplementsInvocationHandler{privateRentrent;publicvoidsetRent(Rentrent){this.rent=rent;}//自动生成代理类,重点是第二个参数,获取要代理的抽象角色!之前都是一个角色,现在可以代理一类角色publicObjectgetProxy(){returnProxy.newProxyInstance(this.getClass().getClassLoader(),rent.getClass().getInterfaces(),this);}//proxy:代理类method:代理类的调用处理程序的方法对象.//处理代理实例上的方法调用并返回结果@OverridepublicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{seeHouse();//核心:本质利用反射实现!Objectresult=method.invoke(rent,args);fare();returnresult;}//看房publicvoidseeHouse(){System.out.println("带房客看房");}//收中介费publicvoidfare(){System.out.println("收中介费");}}Client.java
//租客publicclassClient{publicstaticvoidmain(String[]args){//真实角色Hosthost=newHost();//代理实例的调用处理程序ProxyInvocationHandlerpih=newProxyInvocationHandler();pih.setRent(host);//将真实角色放置进去!Rentproxy=(Rent)pih.getProxy();//动态生成对应的代理类!proxy.rent();}}核心:一个动态代理,一般代理某一类业务,一个动态代理可以代理多个类,代理的是接口!、
我们来使用动态代理实现代理我们后面写的UserService!
我们也可以编写一个通用的动态代理实现的类!所有的代理对象设置为Object即可!
publicclassProxyInvocationHandlerimplementsInvocationHandler{privateObjecttarget;publicvoidsetTarget(Objecttarget){this.target=target;}//生成代理类publicObjectgetProxy(){returnProxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);}//proxy:代理类//method:代理类的调用处理程序的方法对象.publicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{log(method.getName());Objectresult=method.invoke(target,args);returnresult;}publicvoidlog(StringmethodName){System.out.println("执行了"+methodName+"方法");}}测试!
publicclassTest{publicstaticvoidmain(String[]args){//真实对象UserServiceImpluserService=newUserServiceImpl();//代理对象的调用处理程序ProxyInvocationHandlerpih=newProxyInvocationHandler();pih.setTarget(userService);//设置要代理的对象UserServiceproxy=(UserService)pih.getProxy();//动态生成代理类!proxy.delete();}}测试,增删改查,查看结果!
静态代理有的它都有,静态代理没有的,它也有!
什么是AOP
AOP(AspectOrientedProgramming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
Aop在Spring中的作用
以下名词需要了解下:
SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice:
即Aop在不改变原有代码的情况下,去增加新的功能.
使用Spring实现Aop
【重点】使用AOP织入,需要导入一个依赖包!
通过SpringAPI实现
首先编写我们的业务接口和实现类(service包下)
publicinterfaceUserService{publicvoidadd();publicvoiddelete();publicvoidupdate();publicvoidsearch();}publicclassUserServiceImplimplementsUserService{@Overridepublicvoidadd(){System.out.println("增加用户");}@Overridepublicvoiddelete(){System.out.println("删除用户");}@Overridepublicvoidupdate(){System.out.println("更新用户");}@Overridepublicvoidsearch(){System.out.println("查询用户");}}然后去写我们的增强类,我们编写两个,一个前置增强一个后置增强(log包下)
publicclassLogimplementsMethodBeforeAdvice{//method:要执行的目标对象的方法//objects:被调用的方法的参数//Object:目标对象@Overridepublicvoidbefore(Methodmethod,Object[]objects,Objecto)throwsThrowable{System.out.println(o.getClass().getName()+"的"+method.getName()+"方法被执行了");}}publicclassAfterLogimplementsAfterReturningAdvice{//returnValue返回值//method被调用的方法//args被调用的方法的对象的参数//target被调用的目标对象@OverridepublicvoidafterReturning(ObjectreturnValue,Methodmethod,Object[]args,Objecttarget)throwsThrowable{System.out.println("执行了"+target.getClass().getName()+"的"+method.getName()+"方法,"+"返回值:"+returnValue);}}最后去spring的文件中注册,并实现aop切入实现,注意导入约束.
publicclassMyTest{@Testpublicvoidtest(){ApplicationContextcontext=newClassPathXmlApplicationContext("beans.xml");//动态代理代理的是接口UserServiceuserService=(UserService)context.getBean("userService");userService.search();}}Aop的重要性:很重要.一定要理解其中的思路,主要是思想的理解这一块.
Spring的Aop就是将公共的业务(日志,安全等)和领域业务结合起来,当执行领域业务时,将会把公共业务加进来.实现公共业务的重复利用.领域业务更纯粹,程序猿专注领域业务,其本质还是动态代理.
第二种方式
自定义类来实现Aop
目标业务类不变依旧是userServiceImpl
第一步:写我们自己的一个切入类(config包下)
publicclassDiyPointcut{publicvoidbefore(){System.out.println("---------方法执行前---------");}publicvoidafter(){System.out.println("---------方法执行后---------");}}去spring中配置
publicclassMyTest{@Testpublicvoidtest(){ApplicationContextcontext=newClassPathXmlApplicationContext("beans.xml");//动态代理代理的是接口UserServiceuserService=(UserService)context.getBean("userService");userService.add();}}第三种方式
使用注解实现
第一步:编写一个注解实现的增强类
packagecom.sdz.config;importorg.aspectj.lang.ProceedingJoinPoint;importorg.aspectj.lang.annotation.After;importorg.aspectj.lang.annotation.Around;importorg.aspectj.lang.annotation.Aspect;importorg.aspectj.lang.annotation.Before;@AspectpublicclassAnnotationPointcut{@Before("execution(*com.sdz.service.UserServiceImpl.*(..))")publicvoidbefore(){System.out.println("---------方法执行前---------");}@After("execution(*com.sdz.service.UserServiceImpl.*(..))")publicvoidafter(){System.out.println("---------方法执行后---------");}@Around("execution(*com.sdz.service.UserServiceImpl.*(..))")publicvoidaround(ProceedingJoinPointjp)throwsThrowable{System.out.println("环绕前");System.out.println("签名:"+jp.getSignature());//执行目标方法proceedObjectproceed=jp.proceed();System.out.println("环绕后");System.out.println(proceed);}}第二步:在Spring配置文件中,注册bean,并增加支持注解的配置
junit
3、代码实现
编写pojo实体类
packagecom.sdz.pojo;importlombok.Data;@DatapublicclassUser{privateintid;//idprivateStringname;//姓名privateStringpwd;//密码}实现mybatis的配置文件(mybatis-config.xml)
publicinterfaceUserMapper{publicList
packagecom.sdz.utils;importorg.apache.ibatis.io.Resources;importorg.apache.ibatis.session.SqlSession;importorg.apache.ibatis.session.SqlSessionFactory;importorg.apache.ibatis.session.SqlSessionFactoryBuilder;importjava.io.IOException;importjava.io.InputStream;//sqlSessionFactory-->sqlSessionpublicclassMybatisUtils{privatestaticSqlSessionFactorysqlSessionFactory;static{try{//使用Mybatis第一步:获取sqlSessionFactory对象Stringresource="mybatis-config.xml";InputStreaminputStream=Resources.getResourceAsStream(resource);sqlSessionFactory=newSqlSessionFactoryBuilder().build(inputStream);}catch(IOExceptione){e.printStackTrace();}}//既然有了SqlSessionFactory,顾名思义,我们就可以从中获得SqlSession的实例了。//SqlSession完全包含了面向数据库执行SQL命令所需的所有方法。publicstaticSqlSessiongetSqlSession(){returnsqlSessionFactory.openSession();}}测试类
publicclassMyTest{@Testpublicvoidtest(){SqlSessionsqlSession=MybatisUtils.getSqlSession();UserMapperuserMapper=sqlSession.getMapper(UserMapper.class);List
什么是MyBatis-Spring?
MyBatis-Spring会帮助你将MyBatis代码无缝地整合到Spring中。
知识基础
在开始使用MyBatis-Spring之前,你需要先熟悉Spring和MyBatis这两个框架和有关它们的术语。这很重要
MyBatis-Spring需要以下版本:
如果使用Maven作为构建工具,仅需要在pom.xml中加入以下代码即可:
在MyBatis-Spring中,可使用SqlSessionFactoryBean来创建SqlSessionFactory。要配置这个工厂bean,只需要把下面代码放在Spring的XML配置文件中:
在基础的MyBatis用法中,是通过SqlSessionFactoryBuilder来创建SqlSessionFactory的。而在MyBatis-Spring中,则使用SqlSessionFactoryBean来创建。
在MyBatis中,你可以使用SqlSessionFactory来创建SqlSession。一旦你获得一个session之后,你可以使用它来执行映射了的语句,提交或回滚连接,最后,当不再需要它的时候,你可以关闭session。
SqlSessionFactory有一个唯一的必要属性:用于JDBC的DataSource。这可以是任意的DataSource对象,它的配置方法和其它Spring数据库连接是一样的。
一个常用的属性是configLocation,它用来指定MyBatis的XML配置文件路径。它在需要修改MyBatis的基础配置非常有用。通常,基础配置指的是
需要注意的是,这个配置文件并不需要是一个完整的MyBatis配置。确切地说,任何环境配置(),数据源()和MyBatis的事务管理器()都会被忽略。SqlSessionFactoryBean会创建它自有的MyBatis环境配置(Environment),并按要求设置自定义环境的值。
SqlSessionTemplate是MyBatis-Spring的核心。作为SqlSession的一个实现,这意味着可以使用它无缝代替你代码中已经在使用的SqlSession。
模板可以参与到Spring的事务管理中,并且由于其是线程安全的,可以供多个映射器类使用,你应该总是用SqlSessionTemplate来替换MyBatis默认的DefaultSqlSession实现。在同一应用程序中的不同类之间混杂使用可能会引起数据一致性的问题。
可以使用SqlSessionFactory作为构造方法的参数来创建SqlSessionTemplate对象。
publicclassUserDaoImplimplementsUserDao{privateSqlSessionsqlSession;publicvoidsetSqlSession(SqlSessionsqlSession){this.sqlSession=sqlSession;}publicUsergetUser(StringuserId){returnsqlSession.getMapper...;}}按下面这样,注入SqlSessionTemplate:
publicclassUserMapperImplimplementsUserMapper{//sqlSession不用我们自己创建了,Spring来管理privateSqlSessionTemplatesqlSession;publicvoidsetSqlSession(SqlSessionTemplatesqlSession){this.sqlSession=sqlSession;}publicList
publicclassMyTest{@Testpublicvoidtest(){ApplicationContextcontext=newClassPathXmlApplicationContext("spring-dao.xml");UserMappermapper=(UserMapper)context.getBean("userMapper");List
更明确分工整合
spring-dao.xml
mybatis-spring1.2.3版以上的才有这个.
官方文档截图:
dao继承Support类,直接利用getSqlSession()获得,然后直接注入SqlSessionFactory.比起方式1,不需要管理SqlSessionTemplate,而且对事务的支持更加友好.可跟踪源码查看
1、将我们上面写的UserDaoImpl修改一下
publicclassUserMapperImpl2extendsSqlSessionDaoSupportimplementsUserMapper{publicList
@Testpublicvoidtest(){ApplicationContextcontext=newClassPathXmlApplicationContext("applicationContext.xml");UserMappermapper=(UserMapper)context.getBean("userMapper2");List
事务就是把一系列的动作当成一个独立的工作单元,这些动作要么全部完成,要么全部不起作用。
事务四个属性ACID
原子性(atomicity)
一致性(consistency)
隔离性(isolation)
持久性(durability)
将上面的代码拷贝到一个新项目中
在之前的案例中,我们给userMapper接口新增两个方法,删除和增加用户;
//添加一个用户intaddUser(Useruser);//根据id删除用户intdeleteUser(intid);mapper文件,我们故意把deletes写错,测试!
publicclassUserMapperImplextendsSqlSessionDaoSupportimplementsUserMapper{//增加一些操作publicList
@Testpublicvoidtest2(){ApplicationContextcontext=newClassPathXmlApplicationContext("applicationContext.xml");UserMappermapper=(UserMapper)context.getBean("userMapper");List
结果:插入成功!
没有进行事务的管理;我们想让他们都成功才成功,有一个失败,就都失败,我们就应该需要事务!
以前我们都需要自己手动管理事务,十分麻烦!
但是Spring给我们提供了事务管理,我们只需要配置即可;
Spring中的事务管理
编程式事务管理
使用Spring管理事务,注意头文件的约束导入:tx
JDBC事务
事务传播行为就是多个事务方法相互调用时,事务如何在这些方法间传播。spring支持7种事务传播行为:
Spring默认的事务传播行为是PROPAGATION_REQUIRED,它适合于绝大多数的情况。
假设ServiveX#methodX()都工作在事务环境下(即都被Spring事务增强了),假设程序中存在如下的调用链:Service1#method1()->Service2#method2()->Service3#method3(),那么这3个服务类的3个方法通过Spring的事务传播机制都工作在同一个事务中。
就好比,我们刚才的几个方法存在调用,所以会被放在一组事务当中!
配置AOP
导入aop的头文件!
删掉刚才插入的数据,再次测试!
@Testpublicvoidtest2(){ApplicationContextcontext=newClassPathXmlApplicationContext("applicationContext.xml");UserMappermapper=(UserMapper)context.getBean("userMapper");List