admin管理员组文章数量:1794759
JAVA框架Spring 全面详解(学习总结)
Spring 1.Spring 全面详解 1.1简介
◆ Spring:春天 给软件行业带来了春天
◆ 2002,首次推出了Spring框架的雏形:interface21框架
◆ Spring框架即是以interface21框架为基础经过重新设计,并不断丰富其内涵,于2004年3月24日发布了1.0正式版
◆ Rod Johnson Spring Framework创始人,著名作者。 Rod在悉尼大学不仅获得了计算机学位,同时还获得了音乐学位。更令人吃惊的是在回到软件开发领域之前,他还获得了音乐学的博士学位。 有着相当丰富的C/C++技术背景的Rod早在1996年就开始了对JAVA服务器端技术的研究 ◆“轮子理论”,也即“不要重复发明轮子”
◆ Spring理念:使现有的技术更加容易使用,本身是一个大杂烩,整合了现有的技术框架!
◆ SSH :Struct2 + Spring + Hibernate(过时)
◆ SSM:SpringMVC + SPring +Mybatis(现在)
官网:spring.io/ 官网Spring快速入门指南:spring.io/quickstart 官方学习指南(建议收藏):docs.spring.io/spring-framework/docs/current/reference/html/core.html#spring-core 官网API:docs.spring.io/spring-framework/docs/current/javadoc-api/ 官网Spring概述:docs.spring.io/spring-framework/docs/current/reference/html/overview.html#overview 官网项目下载:start.spring.io/ 官方全部版本下载地址:repo.spring.io/release/org/springframework/spring GitHub:github/spring-projects/spring-framework
后面学习需要的jar包
<!-- mvnrepository/artifact/org.springframework/spring-webmvc --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.3</version> </dependency> <!-- mvnrepository/artifact/org.springframework/spring-jdbc --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.3.3</version> </dependency> 1.2 优点◆ Spring是一个开源的免费的框架(容器)! ◆ Spring是一个轻量级的,非入侵式的框架 ◆ 控制反转(IOC),面向切面编程(AOP) ◆ 支持事务的处理,对框架整合的支持!
总结:Spring就是一个轻量级的控制反转(IOC)和切面编程(AOP)的框架!
1.3 组成 1.4 拓展在spring以前的官网有这个介绍:现代化的java开发,说白了就是基于spring开发!
后面需要学习的内容: ◆ Spring Boot ◎ 一个快速开发的脚手架。 ◎ 基于SpringBoot可以快速的开发单个微服务。 ◎ 约定大于配置! ◆ Spring Cloud ◎ SpringCloud是基于SpringBoot实现的。
因为现在大多数公司都在使用SpringBoot进行快速开发,学习SpringBoot的前提,需要完全掌握Spring及SpringMVC!
弊端:发展了太久之后,违背了原来的理念!配置十分繁琐,人称:“配置地狱!
2.IOC 2.1 基本认识:
在Java开发中,IOC 意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。理解好IOC的关键是要明确“谁控制谁,控制什么,为何是反转(有反转就应该有正转了),哪些方面反转了”,那我们来深入分析一下: ● 谁控制谁,控制什么:传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;而IoC是有专门一个容器来创建这些对象,即由Ioc容器来控制对象的创建以及外部资源获取(不只是对象包括比如文件等)。
● 为何是反转,哪些方面反转了:有反转就有正转,传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象:由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转,依赖对象的获取被反转了。
2.2 具体做什么?IoC不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序。
传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;
有了IoC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。
IoC对编程实现由IoC容器帮对象找相应的依赖对象并注入,而不是由对象主动去找。
2.3 理解IoC和DI的关系DI—Dependency Injection,即“依赖注入”:是组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。
理解DI的关键是:“谁依赖谁,为什么需要依赖,谁注入谁,注入了什么”,那我们来深入分析一下:
● 谁依赖于谁:当然是应用程序依赖于IoC容器;
●为什么需要依赖:应用程序需要IoC容器来提供对象需要的外部资源;
● 谁注入谁:很明显是IoC容器注入应用程序某个对象,应用程序依赖的对象;
● 注入了什么:就是注入某个对象所需要的外部资源(包括对象、资源、常量数据)。
IoC和DI由什么关系呢?其实它们是同一个概念的不同角度描述,由于控制反转概念比较含糊(可能只是理解为容器控制对象这一个层面,很难让人想到谁来维护对象关系),所以2004年大师级人物Martin Fowler又给出了一个新的名字:“依赖注入”,相对IoC 而言,“依赖注入”明确描述了“被注入对象依赖IoC容器配置依赖对象”。
2.4 IOC组成理论推导
原来的实现方式 1.UserDao接口
package com.cy.dao; /**接口*/ public interface UserDao { void getUser(); }2.UserDaoImpl实现类
package com.cy.dao; public class UserDaoMysqlImpl implements UserDao{ public void getUser(){ System.out.println("Mysql获取用户信"); } }3.UserService业务接口
package com.cy.service; //业务层 public interface UserService { void getUser(); }4.UserServiceImpl实现类
package com.cy.service; import com.cy.dao.UserDao; import com.cy.dao.UserDaoImpl; import com.cy.dao.UserDaoMysqlImpl; //业务层调Dao层 public class UserServiceImpl implements UserService{ //组合 private UserDao userDao = new UserDaoImpl(); public void getUser() { userDao.getUser(); } }测试:
package com.cy.dao; import com.cy.service.UserService; import com.cy.service.UserServiceImpl; public class MyTest { public static void main(String[] args) { //用户实际调用的是业务层,dao层他们不需要接触! UserService userService = new UserServiceImpl(); userService.getUser(); } }增加需求,增加一个mysql的实现
5.UserDaoMysqlImpl实现类
public class UserDaoMysqlImpl implements UserDao{ public void getUser(){ System.out.println("Mysql获取用户信"); } }修改业务层实现类
//业务层调Dao层 public class UserServiceImpl implements UserService{ //组合 private UserDao userDao = new UserDaoMysqlImpl(); public void getUser() { userDao.getUser(); } }测试:
public class MyTest { public static void main(String[] args) { //用户实际调用的是业务层,dao层他们不需要接触! UserService userService = new UserServiceImpl(); userService.getUser(); } }我们可以发现,成功换到了Mysql ,但是有弊端:我们增加了类,修改了原有的代码
如果现在客户想调用一个新的UserDaoOracleImpl 实现类
public class UserDaoOracleImpl implements UserDao{ @Override public void getUser() { System.out.println("Oracle获取用户数据!"); } }如果要调用这个类,我们又要去业务层修改实现类
//业务层调Dao层 public class UserServiceImpl implements UserService{ private UserDao userDao = new UserDaoOracleImpl(); public void getUser() { userDao.getUser(); } }我们可以发现这种方法特别麻烦,我们因为客户的需求去改变更的代码,这是肯定有问题的,行不通的!
2.5 思考:如果想要改变,就需要每次更改UserServiceImpl
怎么解决这种问题?(解决掉用户?解决掉甲方?解决掉出问题的人?)
怎么让程序不动,让客户端自己去工作?(工厂模式)
修改业务实现类 若将UesrDao使用Set接口实现
//业务层调Dao层 public class UserServiceImpl implements UserService{ private UserDao userDao; //利用set进行动态实现值的注入! public void setUserDao(UserDao userDao) { this.userDao = userDao; } public void getUser() { userDao.getUser(); } }测试:
public class MyTest { public static void main(String[] args) { //用户实际调用的是业务层,dao层他们不需要接触! UserService userService = new UserServiceImpl(); ((UserServiceImpl)userService).setUserDao(new UserDaoMysqlImpl()); userService.getUser(); } }修改调用语句
((UserServiceImpl)userService).setUserDao(new UserDaoOracleImpl());在我们之前的业务中,用户的需求可能会影响我们原来的代码,我们需要根据用户的需求去修改原代码!
如果代码量十分大,修改一次的成本代价,成本十分昂贵!
我们使用一个Set接口实现,已经有了革命性的变化:
private UserDao userDao; //利用set进行动态实现值的注入! public void setUserDao(UserDao userDao) { this.userDao = userDao; }思考:好处在哪里?(dao交给用户控制,程序员不用在修改原程序)
• 之前,程序通过new主动创建对象!控制权在程序猿手上
• 使用set注入后,程序不再具有主动性,而是变成了被动的接受对象!
• 这种思想,从本质上解决了问题,程序员不用再去管理对象的创建了,降低了耦合性!
• 使程序员,可以更加专注在业务的实现上!
• 这就是 IOC 的原型
2.6 IOC的本质
控制反转loC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现loC的一种方法,也有人认为DI只是loC的另一种说法。没有loC的程序中,我们使用面向对象编程,对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了。 loC是Spring框架的核心内容,使用多种方式完美的实现了loC,可以使用XML配置,也可以使用注解,新版本的Spring也可以零配置实现loC。
Spring容器在初始化时先读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,程序使用时再从loc容器中取出需要的对象。
采用XML方式配置Bean的时候,Bean的定义信是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信直接以注解的形式定义在实现类中,从而达到了零配置的目的。
控制反转是一种通过描述(xml或注解)并通过第三方去生产或获取特定对象的方式。在spring中实现控制反转的是IOC容器,其实现方法是依赖注入(Dependency Injection,DI)。
3. HelloSpring 3.1.新建项目spring-02-helloHello 实体类
package com.cy.pojo; public class Hello { private String str; //直接添加toString、get、set }applicationContext.xml文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="www.springframework/schema/beans" xmlns:xsi="www.w3/2001/XMLSchema-instance" xsi:schemaLocation="www.springframework/schema/beans www.springframework/schema/beans/spring-beans.xsd"> <!--使用Spring来创建对象,在Spring这些都称为Bean--> <!-- 类型 变量名 = new 类型(); Hello hello = new Hello(); spring过后编变成: bean = 对象 new Hello(); id=变量名,class=new的对象; property 相当于给对象中的属性设置一个值 --> <bean id="hello" class="com.cy.pojo.Hello"> <property name="str" value="spring"/> </bean> </beans>测试:
public class MyTest { public static void main(String[] args) { //获取spring的上下文对象 ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); //我们的对象现在都在spring中管理,我们要使用,直接去里面取出来 Hello hello = (Hello) context.getBean("hello"); System.out.println(hello.toString()); } }运行结果:
3.2 思考:1.Hello 对象是谁创建的?(Hello 对象是由spring创建的)
2.Hello 对象的属性是怎么设置的?(Hello 对象的属性是由Spring容器设置的)
这个过程就叫控制反转:
● 控制:谁来控制对象的创建,传统应用程序的对象是由程序本身控制创建的,使用Spring后对象由Spring来创建的。
● 反转:程序本身不创建对象,而变成被动的接收对象。
● 依赖注入:就是利用set方法来进行注入的。
● IOC是一种编程思想,由主动的编程变成被动的接收,所谓的IOC,即对象由Spring来创建,管理,装配。
● 可以通过newClassPathXmlApplicationContext去浏览一下底层源码。
生活案例:相当于请客吃饭 原来的这套程序是:你写好菜单买好菜,客人来了自己把菜炒好 招待客人 现在的这套程序是:你告诉楼下餐厅,你要哪些菜,客人来的时候,餐厅把做好的菜,给你送上来 IOC:炒菜这件事情,不再由你自己来做,而是委托给了第三方:餐厅来做
改造:
打开刚才的spring-01-ioc
在resources中添加xml applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="www.springframework/schema/beans" xmlns:xsi="www.w3/2001/XMLSchema-instance" xsi:schemaLocation="www.springframework/schema/beans www.springframework/schema/beans/spring-beans.xsd"> <!--创建两个对象--> <bean id="mysqlImpl" class="com.cy.dao.UserDaoMysqlImpl"/> <bean id="oracleImpl" class="com.cy.dao.UserDaoOracleImpl"/> <!--注入--> <bean id="UserServiceImpl" class="com.cy.service.UserServiceImpl"> <!-- ref:引用Spring容器中创建好的对象 value:具体的值,基本数据类型! ref是动态赋值value是静态赋值 --> <property name="userDao" ref="mysqlImpl"/> </bean> </beans>测试:
public class MyTest { public static void main(String[] args) { //获取ApplicationContext:拿到Spring的容器 ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); //容器在手,天下我有,需要什么就get什么! UserServiceImpl userService= (UserServiceImpl) context.getBean("UserServiceImpl"); userService.getUser(); } }运行结果:
OK,到了现在,我们彻底不用再程序中去改动了,要实现不同的操作,只需要在xml配置文件中进行修改,所谓的loC,一句话搞定:对象由Spring 来创建,管理,装配!
4.IOC创建对象的方式新建一个项目:spring-03-ioc2
1.使用无参构造创建对象(默认) package com.cy.pojo; public class User { private String name; public User(){ System.out.println("User的无参构造"); } public String getName() { return name; } public void setName(String name) { this.name = name; } public void show(){ System.out.println("name="+name); } }创建applicationContext.xml
配置文件上下文,配置完成后左边就有小叶子
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="www.springframework/schema/beans" xmlns:xsi="www.w3/2001/XMLSchema-instance" xsi:schemaLocation="www.springframework/schema/beans www.springframework/schema/beans/spring-beans.xsd"> <bean id="user" class="com.cy.pojo.User"> <property name="name" value="清风"/> </bean> </beans>测试:
public class MyTest { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); User user= (User) context.getBean("user"); user.show(); } } 2.使用有参构造创建对象的三种方式 public class User { private String name; public User(String name){ this.name=name; } public String getName() { return name; } public void setName(String name) { this.name = name; } public void show(){ System.out.println("name="+name); } } 2.1 下标赋值 Constructor argument index <!--下标赋值--> <bean id="user" class="com.cy.pojo.User"> <constructor-arg index="0" value="淡若清风"/> </bean> 2.2 变量类型赋值 Constructor argument type matching <!--第二种:通过类型创建,不建议使用, 如果有2个参数都是String,就无法使用 --> <bean id="user" class="com.cy.pojo.User"> <constructor-arg type="java.lang.String" value="qingfeng"/> </bean> 2.3 变量名称赋值 Constructor argument name <!--第三种,直接通过参数名--> <bean id="user" class="com.cy.pojo.User"> <constructor-arg name="name" value="清风"/> </bean>2.4 扩展
我们再创建一个User2实体类
package com.cy.pojo; public class User2 { private String name; public User2(){ System.out.println("Uset2无参构造被创建了"); } public String getName() { return name; } public void setName(String name) { this.name = name; } public void show(){ System.out.println("name="+name); } } <bean id="user" class="com.cy.pojo.User"> <constructor-arg name="name" value="清风"/> </bean> <bean id="user2" class="com.cy.pojo.User2"> </bean>运行测试结果:
我们可以发现,无参构造没有用它,它也被创建了
对比他们是否相等
User user= (User) context.getBean("user"); User user2= (User) context.getBean("user"); System.out.println(user == user2);小结:
Spring容器,就类似于婚介网站,想要什么类型的对象,直接给婚介说,它给你安排!
在获取spring的上下文对象( new ClassPathXmlApplicationContext(“beans.xml”); )时,spring容器中的所有的对象就已经被创建了。
5.Spring配置说明 5.1 别名 <!--如果添加了别名,通过别名也可以获取对象--> <alias name="user" alias="userAlias"/> public class MyTest { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); User user= (User) context.getBean("userNew"); user.show(); } } 5.2 Bean的配置 <!-- id : bean 的唯一标识符,也就是相当于我们学的对象名 cLass : bean对象所对应的全限定名:包名+类型 name : 也是别名,而且name用时可以取多个别名可以(空格 , ; 分隔) --> <bean id="user2" class="com.cy.pojo.User2" name="user3,user4 user5;user6"> <property name="name" value="淡若清风"/> </bean>
测试:
public class MyTest { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); //User2 user= (User2) context.getBean("user3"); User2 user= (User2) context.getBean("user4"); user.show(); } } 5.3 importimport,一般用于团队开发使用,他可以将多个配置文件,导入合并为一个
假设,现在项目中又多个人开发,这三个人负责不同的类开发,不同的类需要注册在不同的bean中,我们可以用import将所有人的beans.xml合并为一个总的!
● 张三1、李四2、王五3 ● appliacationContext1.xml ● appliacationContext2.xml ● appliacationContext3.xml
合并(内容相同也会合并)
<import resource="applicationContext1.xml"/> <import resource="applicationContext2.xml"/> <import resource="applicationContext3.xml"/>使用的时候,直接使用总的配置就可以了
6.DI依赖注入 6.1 构造器注入
●上面内容已经讲解
6.2 set方式注入 【重点】●依赖注入:Set注入 ●依赖:bean对象的创建依赖于容器! ●注入:bean对象中的所有属性,由容器来注入!
【环境搭建】 1.复杂类型
package com.cy.pojo; public class Address { private String address; public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } @Override public String toString() { return "Address{" + "address='" + address + '\\'' + '}'; } }2.真实测试对象
package com.cy.pojo; public class Student { //普通属性value赋值,普通类ref赋值 private String name; private Address address; private String [] books; private List<String> hobbies; private Map<String, String> card; private Set<String> games; private String wife; //空指针 private Properties info; //以及Getter与Setter方法 }3.applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="www.springframework/schema/beans" xmlns:xsi="www.w3/2001/XMLSchema-instance" xmlns:util="www.springframework/schema/util" xsi:schemaLocation="www.springframework/schema/beans www.springframework/schema/beans/spring-beans.xsd www.springframework/schema/util www.springframework/schema/util/spring-util.xsd"> <bean id="address" class="com.cy.pojo.Address"> <property name="address" value="重庆"/> </bean> <bean id="student" class="com.cy.pojo.Student"> <!--第一种,普通值注入,value--> <property name="name" value="清风"/> <!-- 普通值注入 第二种写法 <property name="name"> <value>清风</value> </property> --> <!--第二种,Bean注入,ref--> <property name="address" ref="address"/> <!--第三种,数组注入--> <property name="books"> <array> <value>红楼梦</value> <value>西游记</value> <value>水浒传</value> <value>三国演义</value> </array> </property> <!--第四种,List注入--> <property name="hobbies"> <list> <value>听歌</value> <value>看电影</value> <value>敲代码</value> </list> </property> <!--第五种,Map注入--> <property name="card"> <map> <entry key="身份证" value="5000020210204656"/> <entry key="手机号" value="6024106834891235"/> </map> </property> <!--第六种,Set注入--> <property name="games"> <set> <value>LOL</value> <value>COC</value> <value>BOB</value> </set> </property> <!--第七种 null注入--> <property name="wife"> <null/> </property> <!-- properties key=value key=value key=value --> <property name="info"> <props> <prop key="学号">1001</prop> <prop key="姓名">清风</prop> <prop key="username">1043051018</prop> <prop key="password">123456789</prop> </props> </property> </bean> </beans>4.测试
package com.cy.pojo; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MyTest { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); Student student = (Student) context.getBean("student"); System.out.println(student); /**运行结果: * Student{ * name='清风', * address=Address{address='重庆'}, * books=[红楼梦, 西游记, 水浒传, 三国演义], * hobbies=[听歌, 看电影, 敲代码], * card={ * 身份证=5000020210204656, * 手机号=6024106834891235 * }, * games=[LOL, COC, BOB], * wife='null', * info={ * 学号=1001, * password=123456789, * 姓名=清风, * username=1043051018 * } * } */ } } 6.3 p、c 标签注入(依赖第三方约束)创建User 实体类
package com.cy.pojo; public class User { private String name; private int age; public User(){} public User(String name, int age) { this.name = name; this.age = age; } //自己导get/set/toString }p标签注入,须在beans中引入 xmlns:p=“www.springframework/schema/p”
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="www.springframework/schema/beans" xmlns:xsi="www.w3/2001/XMLSchema-instance" xmlns:p="www.springframework/schema/p" xsi:schemaLocation="www.springframework/schema/beans www.springframework/schema/beans/spring-beans.xsd"> <!--p命名空间注入,可以直接注入属性的值:property--> <bean id="user" class="com.cy.pojo.User" p:name="清风" p:age="18"/> </beans>先在pom.xm导入junit包
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency>测试:
@Test public void test2(){ ApplicationContext context = new ClassPathXmlApplicationContext("Userapplicat.xml"); User user = context.getBean("user", User.class); System.out.println(user); }运行结果: c标签注入,需在实体中增加有参构造方法,并引入 xmlns:c=“www.springframework/schema/c”
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="www.springframework/schema/beans" xmlns:xsi="www.w3/2001/XMLSchema-instance" xmlns:p="www.springframework/schema/p" xmlns:c="www.springframework/schema/c" xsi:schemaLocation="www.springframework/schema/beans www.springframework/schema/beans/spring-beans.xsd"> <!--p命名空间注入,可以直接注入属性的值:property--> <bean id="user" class="com.cy.pojo.User" p:name="清风" p:age="18"/> <!--c命名空间注入,通过构造器注入:construct-args--> <bean id="user2" class="com.cy.pojo.User" c:name="重庆" c:age="20"/> </beans>测试:
@Test public void test2(){ ApplicationContext context = new ClassPathXmlApplicationContext("Userapplicat.xml"); User user = context.getBean("user2", User.class); System.out.println(user); }注意点:p命名和c命名空间不能直接使用,需要导入xml约束!
xmlns:p="www.springframework/schema/p" xmlns:c="www.springframework/schema/c" 6.4 Bean的作用域创建bean定义时,将创建用于创建实际实例的配方 该bean定义所定义的类的名称。 Bean定义是一个想法 配方很重要,因为它意味着像创建类一样,您可以创建许多对象 单个配方的实例。
1.单例模式(Spring默认机制):仅管理一个singleton bean的一个共享实例,并且所有对bean的请求 一个或多个与该bean定义相匹配的ID会导致该特定bean Spring容器返回的实例。
换句话说,当您定义bean定义并将其范围限定为 单例,Spring IoC容器仅创建对象的一个实例 由该bean定义定义。 该单个实例存储在这样的缓存中 单例bean,以及该命名bean的所有后续请求和引用 返回缓存的对象。 下图显示了单例作用域的工作方式:
官方:
<bean id="accountService" class="com.something.DefaultAccountService"/> <!-- the following is equivalent, though redundant (singleton scope is the default) --> <bean id="accountService" class="com.something.DefaultAccountService" scope="singleton"/>参考自己的:
<bean id="user2" class="com.cy.pojo.User" c:name="重庆" c:age="20" scope="singleton"/>2.原型模式:每次从容器中get对象时,都重新创建
Bean部署的非单一原型范围导致创建新的 每次对该特定bean发出请求时,该bean实例。 即豆 被注入到另一个bean中,或者您通过 getBean()对 容器。 通常,您应该对所有有状态Bean使用原型作用域,并且 无状态bean的单例作用域。
下图说明了Spring原型范围:
(一个数据访问对象 (DAO)通常不配置为原型,因为典型的DAO不能容纳 任何对话状态。 对我们来说,重用核心 单例图。)
以下示例将bean定义为XML原型:
<bean id="accountService" class="com.something.DefaultAccountService" scope="prototype"/>原型模式:每次从容器中get的时候,都会产生一个新对象!
3.其余的request、session、application、websocket这些只能在web开发中使用
7.Bean的自动装配
● 自动装配是spring满足bean依赖的一种方式
● Spring会在上下文中自动寻找,并自动给bean装配属性
◆ 在Spring中由三种装配方式:
1.在xml中显式配置
2.在java中显式配置
3.隐式的自动装配bean 【重要】
7.1 测试:1.环境搭建
● 一个人有两个宠物,一只猫一只狗
创建3个类:
package com.cy.pojo; public class Cat { public void shout(){ System.out.println("喵喵喵~"); } } package com.cy.pojo; public class Dog { public void shout(){ System.out.println("汪汪汪~"); } }封装
package com.cy.pojo; public class People { private Cat cat; private Dog dog; private String name; //自己加get、set、toString }applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="www.springframework/schema/beans" xmlns:xsi="www.w3/2001/XMLSchema-instance" xsi:schemaLocation="www.springframework/schema/beans www.springframework/schema/beans/spring-beans.xsd"> <bean id="cat" class="com.cy.pojo.Cat"/> <bean id="dog" class="com.cy.pojo.Dog"/> <bean id="people" class="com.cy.pojo.People"> <constructor-arg name="name" value="淡若清风"/> <constructor-arg name="dog" value="dog"/> <constructor-arg name="cat" value="cat"/> </bean> </beans>测试:
@Test public void test1(){ ApplicationContext context = new ClassPathXmlApplicationContext( "applicationContext.xml"); People people = context.getBean( "people", People.class); people.getDog().shout(); people.getCat().shout(); }7.2 byName与byType自动装配
byName:会在容器上下文中查找,和自己对象set方法后面的值相对应的beanid
● byName弊端:类型必须唯一
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="www.springframework/schema/beans" xmlns:xsi="www.w3/2001/XMLSchema-instance" xsi:schemaLocation="www.springframework/schema/beans www.springframework/schema/beans/spring-beans.xsd"> <bean id="cat" class="com.cy.pojo.Cat"/> <bean id="dog" class="com.cy.pojo.Dog"/> <bean id="people" class="com.cy.pojo.People" autowire="byName"> <property name="name" value="清风"/> </bean> </beans>byType:会自动在容器上下文中查找,和自己对象属性类型相同的bean ● byType:主要set名字和id相同就能装配 ● 不命名也能装配
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="www.springframework/schema/beans" xmlns:xsi="www.w3/2001/XMLSchema-instance" xsi:schemaLocation="www.springframework/schema/beans www.springframework/schema/beans/spring-beans.xsd"> <bean id="cat111" class="com.cy.pojo.Cat"/> <bean id="dog2222" class="com.cy.pojo.Dog"/> <!-- byName:会在容器上下文中查找,和自己对象set方法后面的值相对应的beanid byType:会自动在容器上下文中查找,和自己对象属性类型相同的bean <bean id="people" class="com.cy.pojo.People" autowire="byName"> <property name="name" value="清风"/> </bean>--> <bean id="people" class="com.cy.pojo.People" autowire="byType"> <property name="name" value="清风"/> </bean> </beans>小结:
byName的时候,需要保证所有bean的id唯一,并且这个bean需要和自动注入的属性的set方法的值一致。
byType的时候,需要保证所有bean的class唯一,并且这个bean需要和自动注入的属性的类型一致。
7.3 使用注解实现自动装配
注释在配置Spring方面比XML更好吗?
基于注释的配置的引入提出了这样一个问题: 这种方法比XML“更好”。 简短的答案是“取决于情况”。 长答案是 每种方法都有其优点和缺点,通常,这取决于开发人员 确定哪种策略更适合他们。 由于定义的方式,注释 在声明中提供了很多背景信,从而使内容更简短 组态。 但是,XML擅长连接组件而不接触其源代码 编码或重新编译它们。 一些开发人员更喜欢布线靠近源头 而另一些人则认为带注释的类不再是POJO,而且, 配置变得分散,难以控制。
注意:注释注入在XML注入之前执行。 因此,XML配置 覆盖通过两种方法连接的属性的注释。
使用注解须知:1.导入约束:context约束
2.配置注解的支持:<context:annotation-config/> 【重要】
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="www.springframework/schema/beans" xmlns:xsi="www.w3/2001/XMLSchema-instance" xmlns:context="www.springframework/schema/context" xsi:schemaLocation="www.springframework/schema/beans www.springframework/schema/beans/spring-beans.xsd www.springframework/schema/context www.springframework/schema/context/spring-context.xsd"> <context:annotation-config/> </beans>@Autowired [常用]
对刚刚的案例进行优化:
导入约束
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="www.springframework/schema/beans" xmlns:xsi="www.w3/2001/XMLSchema-instance" xmlns:context="www.springframework/schema/context" xsi:schemaLocation="www.springframework/schema/beans www.springframework/schema/beans/spring-beans.xsd www.springframework/schema/context www.springframework/schema/context/spring-context.xsd"> <context:annotation-config/> <bean id="cat" class="com.cy.pojo.Cat"/> <bean id="dog111" class="com.cy.pojo.Dog"/> <bean id="people" class="com.cy.pojo.People"/> </beans> public class People { @Autowired private Cat cat; @Autowired private Dog dog; private String name; //自己导入get/toString }测试:
@Test public void test(){ ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); People people=context.getBean("people",People.class); people.getCat().shout(); people.getDog().shout(); }小结:
直接在属性上使用即可!也可以在set方式上使用
使用Autowired我们可以不用编写set方法了,前提是你这个自动装配的属性在IOC(Spring)容器中存在,且符合名字byName
Autowired默认优先按类型匹配,如果多个bean类型相同,就会再按名称匹配
Autowired注解相当于智能识别,,当注入在loC容器中该类型只有一个时。就通过byType进行装配当注入容器,存在多个同一类型的对象时,就是根据byName进行装配
@Nullable
@Nullable 字段标记了这个注解,说明这个字段可以为null;
public @interface Autowired{ boolean required() default true; }测试代码: 如果显示定义了Autowired的required属性为alse,说明这个对象可以为null,否则不允许为空
public class People { //如果定义了Autowired的required属性为false,说明这个对象可以为null,否则不允许为空 @Autowired(required = false) private Cat cat; @Autowired private Dog dog; private String name;@Qualifier
如果@Autowired自动装配的环境比较复杂,自动装配无法通过一个注解【@Autowired】完成的时候,我们可以使用@Qualifier(value = “xxx”)去配置@Autowired的使用,指定一个唯一的bean对象注入!
public class People { @Autowired @Qualifier(value = "cat111") private Cat cat; @Autowired @Qualifier(value = "dog222") private Dog dog; private String name; }@Resource [常用]
@Resource注解,不指定name值,先去判断byName和byType,有一个能注入即成功
public class People { @Resource(name = "cat2") private Cat cat; @Resource private Dog dog; 小结:@Resource和@Autowired的区别
●都是用来自动装配的,都可以放在属性字段上
●@Autowired通过byType的方式实现,而且必须要求这个对象存在!
●@Resource默认通过byName的方式实现,如果找不到名字,则通过byType实现!如果两个都找不到的情况下,就报错!
● 执行顺序不同:@Autowired通过byType的方式实现。@Resource默认通过byName的方式实现。
@Autowired 默认优先按类型匹配,如果多个bean类型相同,就会再按名称匹配 @Nullable 字段标记了这个注解,说明这个字段可以为null; @Qualifier 可以通过@Qualifier(value = “xxx”)去配置@Autowired的使用,指定一个唯一的bean对象注入! @Resource 注解,不指定name值,先去判断byName和byType,有一个能注入即成功
8. 使用注解开发
-
在Spring4之后,要使用注解开发,必须保证aop的包导入了
-
使用注解需要导入context约束,增加注解的支持!
配置环境
1.bean注入使用@Componet注解 //等价于<bean id="user" class="com.cy.pojo.User"> //@Component 组件 @Component public class User { public String name = "清风"; }
测试:
public class MyTest { public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); User user = context.getBean("user", User.class); System.out.println(user.name); } }运行结果:
2.属性注入使用@Value注解 @Component public class User { //@Value("qf") 相当于<property name="name" value="qf"/> @Value("qf") public String name; }
加上set方法
@Component public class User { public String name; @Value("qf") public void setName(String name) { this.name = name; } }3.衍生注解
@Componet有几个衍生注解,我们在web开发中,会按照mvc三层架构分层!
-
dao层 【@Repository】
-
service层 【@Service】
-
controller层 【@Controller】
-
这四个注解功能都是一样的,都是代表将某个类注册到Spring中,装配Bean
4.自动装配
- @Autowired 自动装配通过类型、名字
- 如果Autowired不能唯一自动装配上属性,则需要通过@Qualifier(value=“xxx”)
- @Nullable 字段标记了这个注解,说明这个字段可以为null
- @Resource 自动装配通过名字,类型
5.作用域
- @Scope(“singleton”) 单例
- @Scope(“prototype”) 原型
6.小结
XML 与 注解
- xml更加万能,适用于任何场合!维护简单方便
- 注解不是自己类使用不了, 维护相对复杂
XML 与 注解最佳实践
- xml用来管理bean
- 注解只负责完成属性的注入
- 我们在使用过程中,只需要注意一个问题:必须让注解生效,就需要开启注解的支持
我们现在要完全不使用Spring的xml配置了,全权交给Java来做!
JavaConfig 是Spring的一个子项目,在$pring 4之后,它成为了一个核心功能!
配置环境,新建项目 spring-07
package com.cy.pojo; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; //这个注解的意思,就是说明这个类被spring接管了,注册到了容器中 @Component public class User { private String name; public String getName() { return name; } @Value("淡若清风")//属性注入值 public void setName(String name) { this.name = name; } @Override public String toString() { return "User{" + "name='" + name + '\\'' + '}'; } } //这个也会spring容器托管,注册到容器中,因为他本来就是一个@Component //@configuration代表这是一个配置类,就和我们之前看的beans.xml @Configuration @ComponentScan("com.cy.pojo") @Import(QingConfig2.class) public class QingConfig { //注册一个bean ,就相当于我们之前写的一个bean标签 //这个方法的名字,就相当于bean标签中的id属性 //这个方法的返回值,就相当于bean标签中的class属性 @Bean public User getUser(){ return new User(); } } //这个也会spring容器托管,注册到容器中,因为他本来就是一个@Component //@configuration代表这是一个配置类,就和我们之前看的beans.xml @Configuration @ComponentScan("com.cy.pojo") public class QingConfig2 { //注册一个bean ,就相当于我们之前写的一个bean标签 //这个方法的名字,就相当于bean标签中的id属性 //这个方法的返回值,就相当于bean标签中的class属性 @Bean public User getUser(){ return new User(); } }测试:
package com.cy.test; import com.cy.config.QingConfig; import com.cy.pojo.User; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class MyTest { public static void main(String[] args) { ApplicationContext context = new AnnotationConfigApplicationContext(QingConfig.class); User getUser = context.getBean("getUser",User.class); System.out.println(getUser.getName()); } }10.代理模式
为什么要学代理模式?
因为这就是SpringAOP的底层! 【SpringAOP 和 SpringMVC】
代理模式分类:
- 静态代理
- 动态代理
什么是代理模式?我们来举一个例子 [租客、中介、房东]
10.1 静态代理 角色分析:- 抽象角色:一般会使用接口或者抽象类来解决【房子】
- 真实角色:被代理的角色【房东】
- 代理角色:代理真实角色,代理真实角色后,我们一般会做一些附属操作【中介】
- 客户:访问代理对象的人【租客】
代码步骤:
- 接口【房子】
- 真实角色 【房东】
- 代理角色 【中介】
- 客户端访问代理角色 【租客】
代理模式的优缺点
好处:
- 可以使真实角色的操作更加存粹!不用去关注一些公共的业务
- 公共交给了代理角色,实现了业务的分工
- 公共业务发生扩展的时候,方便集中管理
缺点:
- 一个真实角色就会产生一个代理角色,代码量会翻倍 开发效率变低
代理就是增强方法的类,你每次要增强功能就只能新建一个代理类重新写
10.2 加深印象(换一个案例)
我们创建一个增删改查的功能,然后在此功能实现增加日志的功能(注意不能修改源代码 公司大忌)
public interface UserService { //增删改查 public void add(); public void delete(); public void update(); public void query(); } //真实对象 public class UserServiceImpl implements UserService { @Override public void add() { System.out.println("增加了一个用户"); } @Override public void delete() { System.out.println("删除了一个用户"); } @Override public void update() { System.out.println("修改了一个用户"); } @Override public void query() { System.out.println("查询了一个用户"); } } package com.cy.demo02; //代理对象 public class UserServierProxy implements UserService{ private UserServiceImpl userService; public void setUserService(UserServiceImpl userService) { this.userService = userService; } @Override public void add() { log("add"); userService.add(); } @Override public void delete() { log("delete"); userService.delete(); } @Override public void update() { log("update"); userService.update(); } @Override public void query() { log("query"); userService.query(); } //日志方法 public void log(String msg){ System.out.println("[日志] 使用了"+msg+"方法"); } } //日志功能 public class Client { public static void main(String[] args) { UserServiceImpl userService =new UserServiceImpl(); UserServierProxy proxy = new UserServierProxy(); proxy.setUserService(userService); proxy.query(); proxy.add(); proxy.delete(); proxy.update(); } }图解:
10.3 动态代理
动态代理和静态代理角色一样
动态代理的代理类是动态生成的,不是我们直接写好的
动态代理分为两大类:
-
基于接口的动态代理
-
基于接口:JDK动态代理
-
基于类的动态代理
-
基于类: cglib
-
java字节码实现: javasist
需要了解两个类:
- Proxy:代理
- InvocationHandler:调用处理程序
1.Proxy
java.lang.reflect.Proxy Proxy提供了创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理类的超类。(大白话:这是一个静态类,类里边有方法得到代理类)
动态代理类 (以下简称为代理类 )
- 是一个实现在类创建时在运行时指定的接口列表的类,具有如下所述的行为:
- 代理接口是由代理类实现的接口。
- 代理实例是代理类的一个实例。
- 每个代理实例都有一个关联的调用处理程序对象,它实现了接口InvocationHandler 。
- 通过其代理接口之一的代理实例上的方法调用将被分派到实例调用处理程序的invoke方法,传递代理实例, java.lang.reflect.Method被调用方法的java.lang.reflect.Method对象以及包含参数的类型Object Object的数组。
- 调用处理程序适当地处理编码方法调用,并且返回的结果将作为方法在代理实例上调用的结果返回。
代理类具有以下属性:
- 代理类是公共的,最终的,而不是抽象的,如果所有代理接口都是公共的。
- 如果任何代理接口是非公开的,代理类是非公开的,最终的,而不是抽象的 。
- 代理类的不合格名称未指定。 然而,以字符串"$Proxy"开头的类名空间应该保留给代理类。
- 一个代理类扩展了java.lang.reflect.Proxy 。
- 代理类完全按照相同的顺序实现其创建时指定的接口。
- 如果一个代理类实现一个非公共接口,那么它将被定义在与该接口相同的包中。 否则,代理类的包也是未指定的。 请注意,程序包密封不会阻止在运行时在特定程序包中成功定义代理类,并且类也不会由同一类加载器定义,并且与特定签名者具有相同的包。
- 由于代理类实现了在其创建时指定的所有接口, getInterfaces在其类对象上调用getInterfaces将返回一个包含相同列表接口的数组(按其创建时指定的顺序),在其类对象上调用getMethods将返回一个数组的方法对象,其中包括这些接口中的所有方法,并调用getMethod将在代理接口中找到可以预期的方法。
- Proxy.isProxyClass方法将返回true,如果它通过代理类 - 由Proxy.getProxyClass返回的类或由Proxy.newProxyInstance返回的对象的类 - 否则为false。
- 所述java.security.ProtectionDomain代理类的是相同由引导类装载程序装载系统类,如java.lang.Object ,因为是由受信任的系统代码生成代理类的代码。 此保护域通常将被授予java.security.AllPermission 。
- 每个代理类有一个公共构造一个参数,该接口的实现InvocationHandler ,设置调用处理程序的代理实例。 而不必使用反射API来访问公共构造函数,也可以通过调用Proxy.newProxyInstance方法来创建代理实例,该方法将调用Proxy.getProxyClass的操作与调用处理程序一起调用构造函数。
//为某个接口创建代理Foo : InvocationHandler handler = new MyInvocationHandler(...); Class<?> proxyClass = Proxy.getProxyClass(Foo.class.getClassLoader(), Foo.class); Foo f = (Foo) proxyClass.getConstructor(InvocationHandler.class). newInstance(handler); //或更简单地: Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(), new Class<?>[] { Foo.class }, handler); public static 类<?> getProxyClass(ClassLoader loader, 类<?>... interfaces) throws IllegalArgumentException /*给定类加载器和接口数组的代理类的java.lang.Class对象。 代理类将由指定的类加载器定义,并将实现所有提供的接口。 如果任何给定的接口是非公开的,则代理类将是非公开的。 如果类加载器已经定义了接口相同置换的代理类,那么将返回现有的代理类; 否则,这些接口的代理类将被动态生成并由类加载器定义。 对可能传递给Proxy.getProxyClass的参数有几个Proxy.getProxyClass : interfaces数组中的所有类对象都必须表示接口,而不是类或原始类型。 interfaces数组中没有两个元素可能是指相同的类对象。 所有的接口类型必须通过指定的类加载器的名称可见。 换句话说,对于类加载器cl和每个接口i ,以下表达式必须为真: Class.forName(i.getName(), false, cl) == i 所有非公共接口必须在同一个包中; 否则代理类将不可能实现所有接口,而不管其中定义了什么包。 对于具有相同签名的指定接口的任何成员方法集合: 如果任何方法的返回类型是原始类型或void,则所有方法必须具有相同的返回类型。 否则,其中一个方法必须具有一个返回类型,该类型可以分配给其余方法的所有返回类型。 生成的代理类不能超过虚拟机对类施加的任何限制。 例如,VM可以将类可以实现的接口数量限制为65535; 在这种情况下, interfaces阵列的大小不得超过65535。 如果任何这些限制被违反, Proxy.getProxyClass将抛出一个IllegalArgumentException 。 如果interfaces数组参数或其任何元素为null ,则将抛出一个NullPointerException 。 请注意,指定的代理接口的顺序是重要的:具有相同组合的接口但不同顺序的代理类的两个请求将导致两个不同的代理类。 参数 loader - 类加载器来定义代理类 interfaces - 要实现的代理类的接口列表 结果 在指定的类加载器中定义并实现指定接口的代理类 */ public static Object newProxyInstance(ClassLoader loader, 类<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException /*返回指定接口的代理类的实例,该接口将方法调用分派给指定的调用处理程序。 Proxy.newProxyInstance因为与IllegalArgumentException相同的原因而Proxy.getProxyClass 。 参数 loader - 类加载器来定义代理类 interfaces - 代理类实现的接口列表 h - 调度方法调用的调用处理函数 结果 具有由指定的类加载器定义并实现指定接口的代理类的指定调用处理程序的代理实例 */
2. InvocationHandler
- InvocationHandler是由代理实例的调用处理程序实现的接口 。
- 每个代理实例都有一个关联的调用处理程序。 当在代理实例上调用方法时,方法调用将被编码并分派到其调用处理程序的invoke方法。
- invoke(Object proxy, 方法 method, Object[] args) 处理代理实例上的方法调用并返回结果。
编写实例 //房子 public interface Rent { public void rent(); } //房东 public class landlord implements Rent { public void rent(){ System.out.println("房东要出租房子"); } }
用这个类,自动生成代理类
package com.cy.demo03; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; //等我们会用这个类,自动生成代理类 public class ProxyInvocationHandler implements InvocationHandler { //被代理的接口 private Rent rent; public void setRent(Rent rent){ this.rent=rent; } /*Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(), new Class<?>[] { Foo.class }, handler);*/ //这个代码是死的 public Object getProxy(){ return Proxy.newProxyInstance( //生成对象 new一个 this.getClass().getClassLoader(),//加载到类的哪一个位置 rent.getClass().getInterfaces(),//代表他代理的接口是哪个接口 this);//代表自己这个InvocationHandler } //真正执行的靠这个方法 //生成代理类(处理代理实例,并返回结果) @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //动态代理的本质,就是使用反射机制实现 Object result = method.invoke(rent, args);//invoke实现接口时重写的方法(代理类实例对象调用方法时会调用invoke()方法) seelandlord(); fare(); return result; } public void seelandlord(){ System.out.println("中介带看房子"); } public void fare(){ System.out.println("收中介费"); } }生成代理类
package com.cy.demo03; public class Client { public static void main(String[] args) { //真实角色 landlord host=new landlord(); //代理角色 ProxyInvocationHandler pih = new ProxyInvocationHandler(); //通过调用程序处理角色来处理我们要调用的接口 pih.setRent(host);//这句的意思是:我生成的代理类,我要去实现这个接口 Rent proxy = (Rent) pih.getProxy();//这里的proxy就是动态生成的,我们并没有写 //输出结果 proxy.rent(); } } 优化通过此方法,我们优化之前的增删改查
package com.cy.demo04; import com.cy.demo03.Rent; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; //等我们会用这个类,自动生成代理类 public class ProxyInvocationHandler implements InvocationHandler { //被代理的接口 private Object target; public void setTarget(Object target){ this.target=target; } //生成得到代理类 public Object getProxy(){ return Proxy.newProxyInstance( this.getClass().getClassLoader(), target.getClass().getInterfaces(), this); } //生成代理类(处理代理实例,并返回结果) @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //动态代理的本质,就是使用反射机制实现 log(method.getName()); Object result = method.invoke(target, args); return result; } public void log(String msg){ System.out.println("执行了"+msg+"方法"); } } package com.cy.demo04; import com.cy.demo02.UserService; import com.cy.demo02.UserServiceImpl; public class Client { public static void main(String[] args) { //真实角色 UserServiceImpl userService=new UserServiceImpl(); //代理角色 ProxyInvocationHandler pih = new ProxyInvocationHandler(); //设置要代理的对象 pih.setTarget(userService); //动态生成代理类 UserService proxy = (UserService) pih.getProxy(); proxy.delete(); } }小结:
- ProxyInvocationHandler 生成动态代理实例
- InvocationHandler 调用处理程序,并返回结果
动态代理的好处:
- 可以使真实角色的操作更加纯粹!不用去关注一些公共的业务·公共也就就交给代理角色!实现了业务的分工!
- 公共业务发生扩展的时候,方便集中管理!
- 一个动态代理类代理的是一个接口,一般就是对应的一类业务
11.AOP 11.1 什么是AOP
AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术,AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生泛型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
11.2 Aop在Spring中的作用提供生命事务:允许用户自定义切面
- 横切关注点:跨越应用程序多个模块的方法或功能。即与我们的业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志,安全,缓存,事务等等…
- 切面(ASPECT):横切关注点 被模块化的特殊对象。即 它是一个类 (比如说:后面内容中的Log类)
- 通知(Advice):切面必须要完成的工作,即 他是类中的一个方法(比如说:后面内容中的Log类中的方法)
- 目标(target):被通知的对象 (相当于:一个接口或一个方法)
- 代理(Proxy):向目标对象应用通知之后创建的对象 (相当于:我们之前生成的代理类)
- 切入点(PointCut):切面通知 执行的"地点"的定义 (说白了就是:相当于在哪个地方执行)
- 连接点(jointPoint):与切入点匹配的执行点 (说白了就是:相当于在哪个地方执行)
SpringAop中,通过Advice定义横切逻辑,Spring中支持的5种类型的Advice
11.3 使用Spring实现Aop
【重点】使用AOP织入,需要依赖包
<dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.4</version> </dependency> 方式一:使用Spring的API接口 【主要是spring接口实现】eg:在执行UserService实现类的所有方法时,增加日志功能
UserServer接口
public interface UserService { public void add(); public void update(); public void delete(); public void select(); }UserServer实现类
public class UserServiceImpl implements UserService{ public void add() { System.out.println("增加了一个用户"); } public void update() { System.out.println("更新了一个用户"); } public void delete() { System.out.println("删除了一个用户"); } public void select() { System.out.println("查询了一个用户"); } }Log类
package com.cy.log; import org.springframework.aop.MethodBeforeAdvice; import java.lang.reflect.Method; public class Log implements MethodBeforeAdvice { //Method:要执行的目标对象方法 //object:参数(args: arguments to the method) //target:目标对象 (target:target of the method invocation) @Override public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println(target.getClass().getName() + "的" + method.getName() + "被执行了"); } }AfterLog
package com.cy.log; import org.springframework.aop.AfterReturningAdvice; import java.lang.reflect.Method; public class AfterLog implements AfterReturningAdvice { //returnValue:返回值 //Method:要执行的目标对象方法 //object:参数(args: arguments to the method) //target:目标对象 (target:target of the method invocation) @Override public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { System.out.println("执行了" + method.getName() + "方法,返回值为" + returnValue); } }applicationContext.xml 配置文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="www.springframework/schema/beans" xmlns:xsi="www.w3/2001/XMLSchema-instance" xmlns:aop="www.springframework/schema/aop" xsi:schemaLocation="www.springframework/schema/beans www.springframework/schema/beans/spring-beans.xsd www.springframework/schema/aop www.springframework/schema/aop/spring-aop.xsd"> <!--注册bean--> <bean id="userService" class="com.cy.service.UserServiceImpl"/> <bean id="log" class="com.cy.log.Log"/> <bean id="afterLog" class="com.cy.log.AfterLog"/> <!--方式一:使用原生Spring Api接口--> <!--配置aop--> <aop:config> <!--切入点:execution:表达式, execution(要执行的位置*(修饰词) *(返回值) *(类名) *(方法名) *(参数)) ..任意参数--> <aop:pointcut id="pointcut" expression="execution(* com.cy.service.UserServiceImpl.*(..))"/> <!--执行环绕增加--> <aop:advisor advice-ref="log" pointcut-ref="pointcut"/><!--从哪个类,切入到哪里--> <!--从afterLog类,切入到pointcut中配置的UserServiceImpl--> <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/> </aop:config> </beans>测试:
package com.cy.test; import com.cy.service.UserService; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MyTest { public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); //动态代理必须代理的是接口 UserService userService = context.getBean("userService", UserService.class); userService.add(); } }方式二:自定义来实现AOP【主要是切面定义】
diy
package com.cy.diy; public class DiyPoinCut { public void beforeMethod() { System.out.println("======方法执行之前======"); } public void afterMethod () { System.out.println("======方法执行之后======"); } }applicationContext.xml 配置文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="www.springframework/schema/beans" xmlns:xsi="www.w3/2001/XMLSchema-instance" xmlns:aop="www.springframework/schema/aop" xsi:schemaLocation="www.springframework/schema/beans www.springframework/schema/beans/spring-beans.xsd www.springframework/schema/aop www.springframework/schema/aop/spring-aop.xsd"> <!--注册bean--> <bean id="userService" class="com.cy.service.UserServiceImpl"/> <bean id="log" class="com.cy.log.Log"/> <bean id="afterLog" class="com.cy.log.AfterLog"/> <!--方式二:自定义类--> <bean id="diy" class="com.cy.diy.DiyPoinCut"/> <aop:config> <!--自定义切面,ref 要引入的类--> <!--<aop:aspect ref="diy"> : 标注这个类为切面--> <aop:aspect ref="diy"> <!--切入点,往哪里切入--> <!--切入点:execution:表达式,execution(*(修饰词) *(返回值) *(类名) *(方法名) *(参数)) ..任意参数--> <aop:pointcut id="point" expression="execution(* com.cy.service.UserServiceImpl.*(..))"/> <!--通知,就是类中的一个方法--> <!--before,我们刚刚自己定义的--> <aop:before method="beforeMethod" pointcut-ref="point"/> <aop:after method="afterMethod" pointcut-ref="point"/> </aop:aspect> </aop:config> </beans>测试结果:
方式三:使用注解实现 package com.cy.diy; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.Signature; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; //方式三:使用注解方式实现AOP @Aspect //标注这个类是一个切面 public class Annotation { //@Before("")中的内容就是写切入点 //切入点:execution:表达式,execution(*(修饰词) *(返回值) *(类名) *(方法名) *(参数)) ..任意参数 @Before("execution(* com.cy.service.UserServiceImpl.*(..))") public void before () { System.out.println("====方法执行前===="); } @After("execution(* com.cy.service.UserServiceImpl.*(..))") public void after () { System.out.println("====方法执行后===="); } //在环绕增强中,我们可以给定一个参数,代表我们要获取处理切入的点 @Around("execution(* com.cy.service.UserServiceImpl.*(..))") public void around (ProceedingJoinPoint pjp) throws Throwable { System.out.println("环绕前"); Signature signature = pjp.getSignature();//获得签名 System.out.println("signature" + signature); Object proceed = pjp.proceed();//执行方法 System.out.println("环绕后"); } }
applicationContext.xml 配置文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="www.springframework/schema/beans" xmlns:xsi="www.w3/2001/XMLSchema-instance" xmlns:aop="www.springframework/schema/aop" xsi:schemaLocation="www.springframework/schema/beans www.springframework/schema/beans/spring-beans.xsd www.springframework/schema/aop www.springframework/schema/aop/spring-aop.xsd"> <!--注册bean--> <bean id="userService" class="com.cy.service.UserServiceImpl"/> <bean id="log" class="com.cy.log.Log"/> <bean id="afterLog" class="com.cy.log.AfterLog"/> <!--方式三--> <bean id="Annotation" class="com.cy.diy.Annotation"/> <!--开启注解支持 JDK(默认proxy-target-class="false")cglib默认proxy-target-class="true")--> <aop:aspectj-autoproxy/> <!--另一种写法,很少用,一般默认就好--> <!--<aop:aspectj-autoproxy proxy-target-class="true"/>--> </beans>测试结果:
12.整合mybatis 步骤一:导入相关jar包- junit
- mybatis
- mysql数据库
- spring相关的(webmvc、JDBC)
- aop织入
- mybatis-spring
IDAE连接数据库:如果忘记 看此教程(IDEA连接数据库)
步骤二:编写核心配置文件
- spring-dao.xml配置数据源DataSource与sqlSession
- mybatis-config.xml配置一些mybatis专属配置
- applicationContext.xml整合与注册bean等
- 实体类
- mapper接口
- mapper实现类
- 测试类
SqlSessionDaoSupport 是一个抽象的支持类,用来为你提供 SqlSession。调用 getSqlSession() 方法你会得到一个 SqlSessionTemplate,之后可以用于执行 SQL 方法,就像下面这样:
import java.util.List; public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper { public List<User> selectUser() { SqlSession sqlSession = getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> users = mapper.selectUser(); //return getSqlSession().getMapper(UserMapper.class).selectUser(); return users; } }实际上是整合mybatis一与整合mybatis二是一样的方法,只不过二继承了SqlSessionDaoSupport ,在getSqlSession(),做的也是setSqlSessionTemplate
14 Spring配置声明事务注入 回顾事务- 把一组业务当成一个业务来做;要么都成功,要么都失败!
- 事务在项目开发中,十分的重要,涉及到数据的一致性问题,不能马虎!
- 确保完整性和一致性;
事务的ACID原则:
- 原子性
- 一致性
- 隔离性(多个业务可能操作同一个资源,防止数据损坏)
- 持久性(事务一旦提交,无论系统发生什么问题,结果都不会再被影响,被持久化的写到存储器中!)
版权声明:本文标题:JAVA框架Spring 全面详解(学习总结) 内容由林淑君副主任自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.xiehuijuan.com/baike/1686779253a101762.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论