Spring入门。
程序的耦合和解耦。
1、问题引入。
在使用jdbc和数据库交互时。注册驱动:DriverManager.registerDriver(new com.mysql.cj.jdbc.Driver());
如果把mysql的依赖(jar包)删掉,那么程序在编译期就会出现错误。也就是说当前类,依赖于Driver类。
2、程序的耦合。
耦合:简单来说,就是程序间的依赖关系。包括:类之间的依赖、方法之间的依赖。
解耦:降低程序间的依赖关系。
实际开发中应做到:编译期不依赖,运行时才依赖。
之前注册驱动时,一直用的是 Class.forName("com.mysql.cj.jdbc.Driver")。它依赖的只是一个字符串。
所以,
解耦的思路:
* 第一步:使用反射来创建对象,而 避免使用new关键字。
问题:这样的话,字符串写死了,如果以后用其他数据库驱动,需要改源码。
* 第二部:通过读取配置文件来获取要创建的对象全类名。
3、另一个问题。
通过分析,在之前的三层架构中,各个层之间的调用也有很大的耦合问题。
解决方案:工厂模式解耦。
创建一个 创建Bean对象的工厂。
Bean:代表可重用组件的含义。
JavaBean:用Java编写的可重用组件。JavaBean > 实体类。
第一:需要一个配置文件来配置service和dao。配置的内容:唯一标识=全类名。
第二:通过读取配置文件的内容,反射创建对象。
public class BeanFactory {
private static Properties properties;
static {
properties=new Properties();
InputStream is=BeanFactory.class.getClassLoader().getResourceAsStream("beanfactory.properties");
try {
properties.load(is);
} catch (IOException e) {
e.printStackTrace();
}
}
public static Object getBean(String beanName) {
Object bean=null;
String beanPath=properties.getProperty(beanName);
try {
bean=Class.forName(beanPath).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return bean;
} }
由于这句代码的存在 bean=Class.forName(beanPath).newInstance(); 导致每次用的时候都会创建一个新的对象。
改进方法:将对象先创建完,存入集合中,用的时候直接在集合中拿。、
改进后的工厂模式:
public class BeanFactory {
private static Properties properties;
private static Map<String,Object> beans;
static {
properties=new Properties();
InputStream is=BeanFactory.class.getClassLoader().getResourceAsStream("beanfactory.properties");
try {
properties.load(is);
beans =new HashMap<String, Object>();
Enumeration keys = properties.keys();
while (keys.hasMoreElements()){
String key=keys.nextElement().toString();
String beanPath=properties.get(key).toString();
Object value=Class.forName(beanPath).newInstance();
beans.put(key,value);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static Object getBean(String beanName) {
return beans.get(beanName);
} }
private AcountDao dao=new AcountDaoImp();
private AcountDao dao=(AcountDaoImp)BeanFactory.getBean("AcountDao");
这两种获取对象的形式上,第一种,应用程序直接依赖于资源。而第二种,利用工厂消除了应用程序和资源的直接依赖关系。
第二种方法的思想,就是IOC。
为什么叫控制反转,不叫降低依赖?
因为,对于应用程序来说,用第一种方式创建对象时,有自己的独立控制权,想要谁就要谁,而第二种方式,应用程序失去了控制器,把控制权交给了工厂和配置文件。这种控制权发生的转移,就是控制反转。
Spring的IOC和DI。
控制反转(Inversion of Control,IOC)把创建对象的权力交给框架,是框架的重要特征。
IOC的作用:削减计算机程序的耦合。
Spring基于xml的IOC的环境搭建:
1、导入依赖。
2、配置xml。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="AcountDao" class="AcountDao.AcountDaoImp"></bean>
<bean id="AcountService" class="AcountService.AcountServiceImp"></bean>
</beans>
3、主程序。
public static void main(String[] args) {
//1、获取核心容器对象
ApplicationContext ac=new ClassPathXmlApplicationContext("bean.xml");
//2、根据id获取Bean对象
AcountService acountService=(AcountService)ac.getBean("AcountService");
AcountDao acountDao=ac.getBean("AcountDao",AcountDao.class);
System.out.println(acountService);
System.out.println(acountDao); }
ApplicationContext 的三个常用实现类。
1、ClassPathXmlApplicationContext:它可以加载类路径下的配置文件,要求配置文件必须在类路径下。
2、FileSystemXmlApplicationContext:它可以加载任意路径下的配置文件。
3、AnnotationConfigApplicationContext:用于注解创建容器。
核心容器的两个接口。
1、ApplicationContext:它在构建核心容器时,创建对象采用的策略是立即加载的方式,也就是说,只要一读取完配置文件,马上就创建对象。
2、BeanFactory:它采用延迟加载的方式,什么时候需要,什么时候创建对象。
Spring对Bean的管理细节。
1、创建bean 的三种方式。
* 使用默认构造函数创建:在spring配置文件中使用bean标签,配以id和class属性,且没有其他属性和标签时,采用的就是默认构造函数创建bean对象,此时如果类中没有默认构造函数,则无法创建。
* 使用普通工厂的方法创建对象(使用某个类的方法创建其他类对象):在bean标签中使用 factory-bean和factory-method 属性指定类和方法。
* 使用工厂的静态方法创建对象(使用某个类的静态方法创建对象):使用class和factory-method属性。
2、Bean的作用范围。
bean标签的scope属性取值:
* singleton:单例的(默认值) 常用
* prototype:多例的。常用
* request:作用于web应用的请求范围。
* session:作用于web应用的会话范围。
* global-session:作用于集群环境的会话范围。
3、Bean对象的生命周期。
* 单例对象。
出生:当容器创建时对象出生。
活着:只要容器存在,对象就一直存在。
死亡:容器销毁,对象销毁。
总结:单例对象的生命周期和容器相同。
* 多例对象。
出生:要使用对象时创建。
活着:对象 只要在使用过程中就一直活着。
死亡:对象长时间不用,且没有被引用时,由jvm回收。
Spring中的依赖注入。
IOC的作用:降低程序间的依赖关系(耦合)
依赖关系的管理:以后都交给spring来维护。
在当前类需要用到其他类的对象时,由spring为我们提供,我们只需在配置文件中说明。
依赖关系的维护就称之为依赖注入。
依赖注入:
1、能注入的数据,有三类:
*基本类型和String
*其他bean类型(在配置文件中或注解配置过的bean)
*复杂类型/集合类型
2、注入的方式,有三种:
第一种:使用构造函数提供
使用的标签:constructor-arg
标签的位置:bean里
标签中的属性:
* type:指定注入数据的类型,也是构造函数中参数的类型。
* index:要注入的数据在构造函数中的位置。
* name:构造函数中指定参数。
* value:用于提供基本类型和String类型的数据。
* ref:用于指定其他的bean类型数据。
<bean id="AcountService" class="AcountService.AcountServiceImp">
<constructor-arg name="name" value="rt"></constructor-arg>
<constructor-arg name="age" value="21"></constructor-arg>
<constructor-arg name="birthday" ref="birthday"></constructor-arg>
</bean>
<bean id="birthday" class="java.util.Date"></bean>
优势:获取bean对象时,注入数据是必须的操作。
弊端:改变了bean对象的实例化方式,如果创建对象时用不到这些数据,也必须提供。
第二种:使用set方法提供 更常用
涉及的标签:property
出现的位置:bean里
标签的属性:
* name:指定注入时所调用的set方法名称。
* value:用于提供基本类型和String类型的数据。
* ref:用于指定其他的bean类型数据。
优势:创建对象时没有明确的限制,可以直接使用默认的构造函数。
弊端:如果某个成员必须有值,则获取对象时有可能set方法没有执行。
复杂类型/集合类型的注入:
*用于给list结构的集合注入的标签:list arry set
*用于给map结构的集合注入的标签:map props
第三种:使用注解提供
用于创建对象的注解: 它们的功能和<bean>标签实现的功能一样
1、@Component:把当前类对象存入spring容器中。
属性:value:用于指定bean的id。默认是当前类名,且首字母小写。
2、@Controller:一般用于表现层
3、@Service:一般用在业务层
4、@Repository:一般用在持久层
以上三个注解的作用和@Componet一样,是Spring为我们提供的明确三层使用的注解,
使我们的三层对象更加清晰。
需要的xml配置:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd"> <!--告知spring在创建容器时要扫描的包,配置所需的标签不是在beans的约束中,而是
一个名称为context名称空间和约束中-->
<context:component-scan base-package="AcountService"></context:component-scan> </beans>
用于注入数据的注解:它们的作用和bean中的<property>标签的作用一样
1、@Autowired:
* 作用:自动按类型注入。
* 只要容器中有唯一的一个bean对象类型和要注入的变量类型匹配,就可以成功注入。
* 如果ioc容器中没有任何bean的类型和要注入的变量类型匹配,则报错。
* 如果ioc容器中有多个匹配的类型时,在将变量名作为bean的id,继续查找匹配项。
* 出现位置:变量上,或者方法上。
2、@Qualifier:
* 作用:在按照类型注入的基础上再按照名称注入。它在给类成员注入时不能单独使用(需要AotoWired)。但是在给方法参数注入时可以。
* 属性:value:用于指定注入bean的id。
3、@Resource:
* 作用:直接按照bean的id注入,可以单独使用。
* 属性:name:bean的id。
4、@Value:
* 作用:用于注入基本类型和String类型的数据。
* 属性:value:用于指定数据的值。可以使用spring的SpEL表达式。${表达式}
用于改变作用范围的注解:它们的作用和bean中的scope属性作用一样
1、@Scope:
* 作用:指定bean的作用范围。
* 属性:value:指定范围的取值:singleleton prototype
和生命周期相关的注解:它们的作用和bean中的init-method 和 destroy-method 属性的作用一样
1、@PreDestroy
* 作用:用于指定销毁方法
2、@PostConstruct
* 作用:用于指定初始化方法
Spring中的新注解:
虽然上面的描述中已经使用了注解来配置,但是任然需要xml文件。
解决方案:创建一个配置类。
1、@Configuration
* 作用:指定当前类是一个配置类。
2、@ComponetScan
* 作用:指定spring在创建容器时需要扫描的包
* 属性:value:它和basePackeages的作用一样,都用于指定创建容器时需要扫描的包,相当于xml的:<context:component-scan base-package="AcountService"></context:component-scan>
3、@Bean
* 作用:将当前方法的返回值作为bean对象存入ioc容器。
* 属性:name:指定bean 的id,默认是方法名称。
* 细节:使用注解配置方法时,如果方法有参数,spring会在容器中查找有没有可用的bean对象,查找方式和Aoutowired一样。
4、@Import
* 作用:用于导入其他配置类。
* 属性:value:指定其他配置类的字节码。、
使用Import注解的类是父配置类,导入的是子配置类。
5、@PropertySource
* 作用:用于指定properties文件的位置。
* 属性:value:文件的名称和路径。 关键字:classpath:表示类路径下。
Spring整合Junit的配置。
1、导入jar坐标。
2、使用Junit提供的一个注解把原有的main方法替换成spring提供的。
@Runwith(SpringJUnit4ClassRunner.clsss)
3、告知spring的运行器,spring和ioc创建是基于xml还是基于注解,且说明位置。
@ContextConfiguration
location:指定xml文件的位置,加上classpath,表示在类路径下。
classes:指定注解类所在位置。
当使用spring5.x时,要求junit必须是4.12及以上。
AOP:
动态代理:
特点:字节码随用随创建,随用随加载.
作用:不修改源码的基础上对方法增强.
分类:基于接口的动态代理 基于子类的动态代理.
基于接口的动态代理:
1.涉及的类:Proxy
2.提供者:JDK
3.如何创建代理对象:newProxyInstance方法.
4.创建代理对象的要求:被代理类最少实现一个接口.
5.newProxyInstance方法的参数:
* ClassLoader:类加载器. 用于加载代理对象的字节码,和被代理对象使用相同的类加载器. 固定写法.
* Class[] :字节码数组. 用于让代理对象和被代理对象有相同的方法. 固定写法.
* InvocationHandler: 用于提供增强的代码. 它是让我们写如何代理. 一般都是写一个该接口的实现类,通常是匿名内部类,但不是必须的.
6.invoke方法的作用: 执行被代理对象的任何接口方法都经过改方法(拦截作用)
7.invoke方法的参数:
* proxy:代理对象的引用.
* method: 当前执行的方法.
* args :当前执行方法所需的参数.
* 返回值: 和被代理对象方法有相同的返回值.
问题引入:
在转账案例中,分析可知,应该使用ThreadLocal对象把Connection和当前线程绑定,从而使一个线程只能有一个能控制事务的对象。
AOP概述:
AOP:Aspect Oriented Programming 。面向切面编程。
作用:在程序运行期间,不修改源码对已有的方法进行增强。
优势:减少重复代码,提高开发效率,维护方便。
实现方式:动态代理。
Spring中的AOP:spring中的AOP就是通过配置的方式,实现AOP功能 。
1、引入依赖。
2、开启基于注解的AOP功能<aop:aspectj-autoproxy>
3、写切面类(重复的模块),并用存入容器,@Component ,以及声明为切面类 @Aspect
4、申明切面类的各种通知(方法)是什么时候执行(@before....),然后声明在切入哪个方法(切入点表达式):@before(“execution(修饰符 返回值 方法全类名)”)。可以在通知方法中声明类型为 JoinPoint的参数,用来访问被代理方法的参数。
AOP中的细节:
1、有接口时,使用的对象并非原对象,而是代理对象,它们之间唯一的关系是实现了同一个接口,所以要用接口类型来接收。没有接口时,用的是cglib创建的代理对象,可以用原对象来接收。
2、切入点表达式:
i:可以使用通配符:* :1、匹配任意字符 Mymethod* :以Mymethod开头的所有方法。 2、匹配任意一个参数(int ,*) 一个int 一个任意。
.. :1、匹配任意个参数(..) 2、匹配任意多层路径 com.sicau..mymethod(..)
3、切入方法执行顺序:正常:@before @after(finally里面) @afterreturnning(方法返回后) 异常:@before @after(finally里面) @afterthrowing
4、可以使用JoingPoint 获取当前执行方法对象。
5、通知方法的格式要求是不严谨的,唯一要注意的是,参数不能乱写,因为最终方法是由spring调用,它要知道具体参数是什么,比如你要获得异常,需要在注解上申明:throwing=变量名。
6、抽取可重用的切入点表达式:随便申明一个void 方法 ,方法加@PointCut(表达式),引用:直接用方法名引用。
7、环绕通知,通知上加@Around,它就是一个动态代理,ProceedingJoinPoint 对象.proceed ,就是相当于invoke方法,可以在方法周围写通知。
8、环绕通知在普通通知之前执行,因为普通通知是在proceed方法执行后才能执行的。
9、多个切面同时作用时,可以使用@Order ,数字越小,越优先执行,执行过程相当于嵌套过程。
Spring中的事务:
1、事务回滚:默认发生运行时异常会回滚,但但发生编译时异常不会回滚。@Translational 中的属性:norollbackfor={class} 哪些异常可以不回滚。rollbackfor={class} 哪些异常要回滚。
2、事务传播行为:事务里有其他事务时, propagation=Propagation.Require 使用当前事务 propagation=Propagation.Require_new 使用新事务。
使用require时,事务的属性都是继承大事务的,自己设的属性无效。
Spring入门。的更多相关文章
- Spring入门学习(一)
SpringMVC基础平台补充(2016.03.03) 如果想要开发SpringMVC,那么前期依次安装好:JDK(jdk-8u74-windows-x64,安装后配置环境变量JAVA_HOME和CL ...
- Spring入门(10)-Spring JDBC
Spring入门(10)-Spring JDBC 0. 目录 JdbcTemplate介绍 JdbcTemplate常见方法 代码示例 参考资料 1. JdbcTemplate介绍 JdbcTempl ...
- Spring入门(9)-AOP初探
Spring入门(9)-AOP初探 0. 目录 什么是面向切面编程 AOP常见术语 AOP实例 参考资料 1. 什么是面向切面编程 Aspect Oriented Programming(AOP),即 ...
- Spring入门(8)-基于Java配置而不是XML
Spring入门(8)-基于Java配置而不是XML 本文介绍如何应用Java配置而不是通过XML配置Spring. 0. 目录 声明一个简单Bean 声明一个复杂Bean 1. 声明一个简单Bean ...
- Spring入门(7)-自动检测Bean
Spring入门(7)-自动检测Bean 本文介绍如何自动检测Bean. 0. 目录 使用component-scan自动扫描 为自动检测标注Bean 1. 使用component-scan自动扫描 ...
- Spring入门(6)-使用注解装配
Spring入门(6)-使用注解装配 本文介绍如何使用注解装配. 0. 目录 使用Autowired 可选的自动装配 使用Qualifier选择 1. 使用Autowired package com. ...
- Spring入门(5)-自动装配Bean属性
Spring入门(5)-自动装配Bean属性 本文介绍如何装配Bean属性. 0. 目录 ByName ByType constructor 默认自动装配 混合使用自动装配和显示装配 1. ByNam ...
- Spring入门(4)-注入Bean属性
Spring入门(4)-注入Bean属性 本文介绍如何注入Bean属性,包括简单属性.引用.内部Bean.注入集合等. 0. 目录 注入简单值 注入引用 注入内部Bean 装配集合 装配空值 使用命名 ...
- Spring入门(3)-Spring命名空间与Bean作用域
Spring入门(3)-Spring命名空间与Bean作用域 这篇文章主要介绍Spring的命名空间和Bean作用域 0. 目录 Spring命名空间 Bean作用域 1. Spring命名空间 在前 ...
- Spring入门(2)-通过构造器注入Bean
Spring入门(2)-通过构造器注入Bean 前一篇文章将了最基本的spring例子,这篇文章中,介绍一下带有参数的构造函数和通过构造器注入对象引用. 0. 目录 带有参数的构造函数 通过构造器注入 ...
随机推荐
- LeetCode237-Delete_Node_In_A_Linked_List
delete-node-in-a-linked-list public void deleteNode(ListNode node) { node.val = node.next.val; node. ...
- pointnet++之classification/train.py
1.数据集加载 if FLAGS.normal: assert(NUM_POINT<=10000) DATA_PATH = os.path.join(ROOT_DIR, 'data/modeln ...
- AWS云教育账号创建以及搭建数据库
注册过程繁琐,本文强调关键几点 首先拿到aws的二维码,进入之后填写相关个人信息,用学校邮箱注册,用学校邮箱注册!! 之后审核会有大约10分钟的过程,之后会收到确认邮件 点进去之后就可以设置自己的密码 ...
- [知识点]最近公共祖先LCA
UPDATE(20180822):重写部分代码. 1.前言 最近公共祖先(LCA),作为树上问题,应用非常广泛,而求解的方式也非常多,复杂度各有不同,这里对几种常用的方法汇一下总. 2.基本概念和暴力 ...
- Boost Graph Library使用学习
Boost Graph Library,BGL 使用学习 探索 Boost Graph Library https://www.ibm.com/developerworks/cn/aix/librar ...
- A1083 List Grades (25 分)
Given a list of N student records with name, ID and grade. You are supposed to sort the records with ...
- [LeetCode] 290. Word Pattern 词语模式
Given a pattern and a string str, find if str follows the same pattern. Here follow means a full mat ...
- linux内核动态调试技术
动态调试功能就是你可以决定在程序运行过程中是否要 pr_debug(), dev_dbg(), print_hex_dump_debug(), print_hex_dump_bytes() 这些函数正 ...
- CentOS7 Zookeeper 安装
集群部署 192.168.38.6 zk01192.168.38.7 zk02192.168.38.8 zk03 安装zookeeper,必须安装jdk. 1.下载 $ cd /usr/loca ...
- Java连载17-赋值类运算符&字符串的连接运算符
一.赋值运算符 1.赋值类运算符包括两种: (1)基本赋值运算符:= (2)扩展的赋值运算符: += -= *= /= &= 赋值类的运算符优先级:先执行等号右边的表 ...