问题一: 层与层之前联系在一起,耦合性高(接口与实现类相互联系)
解决方法: Spring提供了第三方来自动加载对象,不用自己手动new出对象,第三方自动提供Bean对象
问题二:通用的事务功能耦合在业务代码中,通用的日志功能耦合在业务代码中
- 通用的事务功能指的是一些通用的数据库事务操作,比如开启事务、提交事务、回滚事务等。将这些通用的事务功能耦合在业务代码中意味着业务代码需要自己实现这些事务操作,导致业务代码与事务代码混杂在一起,代码可读性和可维护性降低。
- 这种情况下,业务代码需要关注事务的处理,而不是专注于业务逻辑本身,从而增加了代码的复杂度和维护难度。此外,如果多个业务模块都需要使用同样的事务功能,那么这些业务模块都需要独立实现,增加了代码重复度和开发成本。
解决方法: 程序当中不用自己手动去new出对象,第三方可以根据需求去创建程序需要的Bean对象的代理对象
IoC
:lnversion of Control,控制反转,强调的是原来在程序中创建Bean的权利反转给第三方。DI
:Dependency Injection,依赖注入,强调的Bean之间关系,这种关系第三方负责去设置。AOP
:Aspect Oriented Programming,面向切面编程,功能的横向抽取,主要的实现方式就是Proxy通用
解决方案;半成品
,使用框架规定的语法开发可以提高开发效率可以用简单的代码就能完成复杂的基础业务;扩展性
;Java中常用的框架:
不同语言,不同领域都有属于自己的框架,使用框架开发是作为程序员的最基础的底线,基本所有的程序员都需要掌握一种框架给予自己开发。
Java语言中的框架,可以分为基础框架和服务框架:
基础框架
:完成基本业务操作的框架,如MyBatis、Spring、SpringMVC、Struts2、Hibernate等服务框架
:特定领域的框架,一般还可以对外提供服务框架,如MQ、ES、Nacos等spring是一个开源的
轻量级
Java开发应用框架,可以简化企业级应用开发。Spring解决了开发者在JavaEE开发中遇到的许多常见的问题,提供了功能强大IOC、AOP及Web MVC等
功能。是当前企业中Java开发几乎不能缺少的框架之一。Spring的生态及其完善,不管是Spring哪个领域的解决方案都是依附于在Spring Framework
基础框架的。
1. 导入Spring当中的jar包
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.7</version>
</dependency>
2. 定义userService和其实现的接口userServiceImpl
public interface UserService {
}
public class UserServiceImpl implements UserService {
}
3. 创建bean.xml配置文件,将UserServiceImpl信息配置到xml当中
<bean id="userService" class="com. itheima.service.impl.UserServiceImpl"></bean>
4. 编写测试代码,创建BeanFactory,加载配置文件,获取UserService实例对象。
//创建工厂对象(后面会被applicationContext替代)
DefaultListableBeanFactory BeanFactory = new DefaultListableBeanFactory();
//创建一个读取器(XML文件)
XmlBeanDefinitionReader Reader = new XmlBeanDefinitionReader(BeanFactory);
//读取配置文件给工厂
Reader.loadBeanDefinitions("beans.xml");
//根据id获取Bean实例对象
UserService userService = (UserService)BeanFactory.getBean("UserService");
setUserDao
的方法public class UserServiceImpl implements UserService {
public void setUserDao(UserDao userDao){
System.out.println("BeanFactory调用方法获取userDao设置到此处"+userDao);
}
}
配置注入<bean id="UserService" class="com.huanglei.service.Impl.UserServiceImpl">
<property name="userDao" ref="UserDao"/>
bean>
<bean id="UserDao" class="com.huanglei.Dao.Impl.UserDaoImpl">bean>
//创建工厂对象
DefaultListableBeanFactory BeanFactory = new DefaultListableBeanFactory();
//创建一个读取器(XML文件)
XmlBeanDefinitionReader Reader = new XmlBeanDefinitionReader(BeanFactory);
//读取配置文件给工厂
Reader.loadBeanDefinitions("beans.xml");
//根据id获取Bean实例对象
UserService userService = (UserService)BeanFactory.getBean("UserService");
ApplicationContext 称为Spring容器
,内部封装了BeanFactory
,比BeanFactory功能更丰富更强大,使用ApplicationContext进行开发时,xml配置文件的名称习惯写成applicationContext.xml
利用ApplicationContext创建IoC容器的基本操作:
//创建ApplicationContext,加载配置文件,实例化容器
ApplicationContext applicationContext = new ClassPathxmlApplicationContext("applicationContext.xml");
//根据beanName获得容器中的Bean实例
UserService userService = (UserService) applicationContext.getBean("UserService");
System.out.println(userService) ;
BeanFactory
是Spring的早期接口,称为Spring的Bean工厂,ApplicationContext
是后期更高级接口,称之为Spring容器;功能进行了扩展
,例如:监听功能、国际化功能等。BeanFactory
的API更偏向底层,ApplicationContext
的API大多数是对这些底层API的封装;ApplicationContext与BeanFactory既有继承关系,又有融合关系。
BeanFactory
是在首次调用getBean时才进行Bean的创建,ApplicationContext
则是配置文件加载,容器一创建就将Bean都实例化并初始化好。ApplicationContext除了继承了BeanFactory外,还继承了ApplicationEventPublisher(事件发布器)、ResouresPatternResolver(资源解析器)、MessageSource(消息资源)等。但是ApplicationContext的核心功能还是BeanFactory。
BeanFactory是核心接口,项目运行过程中肯定有具体实现参与,这个具体实现就是DefaultListableBeanFactory,而ApplicationContext内部维护的Beanfactory的实现类也是它
只在Spring基础环境下,即只导入spring-context坐标时,此时ApplicationContext的继承体系
只在Spring基础环境下,常用的三个ApplicationContext作用如下:
实现类 | 功能描述 |
---|---|
ClassPathXmlApplicationContext | 加载类路径下的xml配置的ApplicationContext |
FileSystemXmlApplicationContext | 加载磁盘路径下的xml配置的ApplicationContext |
AnnotationConfigApplicationContext | 加载注解配置类的ApplicationContext |
如果Spring基础环境中加入了其他组件解决方案,如web层解决方案,即导入spring-web坐标,此时ApplicationContext的继承体系
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webartifactId>
<version>5.3.7version>
dependency>
只在Spring的Web环境下,常用的两个ApplicationContext作用如下:
实现类 | 功能描述 |
---|---|
XmlWebApplicationContext | web环境下,加载类路径下的xml配置的ApplicationContext |
AnnotationConfigWebApplicationContext | web环境下,加载磁盘路径下的xml配置的ApplicationContext |
Xml配置方式 | 功能描述 |
---|---|
|
Bean的id和全限定名配置 |
|
通过name设置Bean的别名,通过别名也能直接获取到Bean实例 |
|
Bean的作用范围, BeanFactory作为容器时取值singleton和prototype |
|
Bean的实例化时机,是否延迟加载。BeanFactory作为容器时无效 |
|
Bean实例化后自动执行的初始化方法,method指定方法名 |
|
Bean实例销毁前的方法,method指定方法名 |
|
设置自动注入模式,常用的有按照类型byType,按照名字byName |
|
指定哪个工厂Bean的哪个方法完成Bean的创建 |
Bean的基础配置
例如:配置UserDaolmpl由Spring容器负责管理
<bean id="userDao" class="com.itheima.dao .impl.UserDaoImp! " />
此时存储到Spring容器(singleObjects单例池)中的Bean的beanName是userDao,值是UserDaolmpl对象,可以根据beanName获取Bean实例
applicationContext.getBean("userDao");
如果不配置id,则Spring会把当前Bean实例的全限定名作为beanName
applicationContext.getBean("com.itheima.dao.impl.UserDaoImpl");
Bean的别名配置
可以为当前Bean指定多个别名,根据别名也可以获得Bean对象
<bean id="userDao" name="aaa,bbb" class="com.itheima.dao.impl.UserDaoImp1" />
此时多个名称都可以获得UserDaolmpl实例对象
applicationContext.getBean("userDao");
applicationContext.getBean("aaa");
applicationContext.getBean("bbb");
总结:
- 如果有
id
,那么beanName就是指的是qiid
,但是也可以用name
别名的方式进行调用- 如果没有
id
,那么就可以调用Bean实例的权限名
或name进行调用BeanName
默认情况下,单纯的Spring环境Bean的作用范围有两个: Singleton
和Prototype
singleton:单例
,默认值,Spring容器创建的时候,就会进行Bean的实例化,并存储到容器内部的单例池中,每次getBean时都是从单例池中获取相同的Bean实例;prototype:原型
,Spring容器初始化时不会创建Bean实例当调用getBean时才会实例化Bean,每次getBean都会创建一个新的Bean实例。lazy-init
设置为
<bean id="userDao" class=" com.itheima.dao.impl.UserDaoImpl" lazy-init="true"/>
Bean在被实例化后,可以执行指定的初始化方法完成一些初始化的操作,Bean在销毁之前也可以执行指定的销毁方法完成一些操作,初始化方法名称和销毁方法名称通过
<bean id="userDao" class=" com.itheima.dao .impl.UserDaoImpl" init-method="init" destroy-method="destroy" />
public class UserDaoImpl implements UserDao {
public UserDaoImpl() { System.out.println("UserDaoImpl创建了...");}
public void init(){ System.out.println("初始化方法...");}
public void destroy(){ system.out.println("销毁方法...");}
}
扩展:除此之外,我们还可以通过实现InitializingBean接口,完成一些Bean的初始化操作,如下:
public class UserDaoImpl implements UserDao,InitializingBean {
public UserDaoImpl(){System.out.println("UserDaoImpl创建了...");}
public void init(){System.out.println("初始化方法...");}
public void destroy(){System.out.println("销毁方法...");}
//执行时机早于init-method配置的方法
public void afterPropertiesSet() throws Exception {
System.out.println("InitializingBean...");
}
}
综上所述,我们现在有两种初始化操作:
- init-method=“init” destroy-method=“destroy”
- 调用接口InitializingBean实现当中的方法
调用顺序: 接口方法InitializingBean > init-method的方法
Bean的实例化配置
构造方式实例化Bean又分为无参构造方法
实例化和有参构造方法
实例化,Spring中配置的
几乎都是无参构造该方式 。下面讲解有参构造方法实例化Bean
//有参构造方法
public UserDaoImpl (String name) {}
有参构造在实例化Bean时,需要参数的注入,通过
标签,嵌入在标签内部提供构造参数,如下:
<bean id="userDao" class="com.itheima.dao .impl.UserDaoImpl">
<constructor-arg name="name" value="haohao" />
</bean>
与
和
都是用来注入依赖的标签,但是它们的使用场景不同。
用于构造函数注入,即在创建对象时通过构造函数传入依赖。它可以指定参数的类型、索引或者参数名,用于匹配构造函数的参数列表。
用于属性注入,即在创建对象后通过 setter 方法设置依赖。它可以指定属性名或者索引,用于匹配对象的 setter 方法。因此,如果一个类只有一个构造函数,并且这个构造函数的参数都是必需的,那么可以使用
进行注入;如果一个类有多个构造函数,或者有一些可选的依赖,那么可以使用
进行注入。
工厂方式实例化Bean,又分为如下三种:
public class FactoryDemo1 {
public static UserService CreateUserService(){
return new UserServiceImpl();
}
}
<bean id="FactoryDemo1" class="com.Smulll.Factory.FactoryDemo1" factory-method="CreateUserService">bean>
优点:
1. 可以在创建对象之前执行一些其他的逻辑操作
2. 可以生成一些jar包或者工具类使用静态方法创建的对象
使用该工厂方法的优点:
1. 可以执行一些创建对象之前其他的逻辑操作
2. 可以生成一些其他工具类或jar包通过方法来创造的对象
public class FactoryDemo2 {
public UserService CreateUserService(){
return new UserServiceImpl();
}
}
<!--配置实例工厂方法-->
<!--先设置工厂对象-->
<bean id="factoryDemo2" class="com.huanglei.Factory.FactoryDemo2"></bean>
<!--在设置工厂方法-->
<bean id="factoryUser" factory-bean="factoryDemo2" factory-method="CreateUserService"></bean>
<bean id="factoryUser" factory-bean="factoryDemo2" factory-method="CreateUserService">
<constructor-arg name="name" value="zhangsan"/>
</bean>
使工厂类继承FactoryBean
通过getObject()方法返回对象
public class FactoryDemo3 implements FactoryBean<UserDao> {
@Override
public UserDao getObject() throws Exception {
return new UserDaoImpl();
}
@Override
public Class<?> getObjectType() {
return null;
}
}
这个方法有延迟性,只有使用getBean
方法调用时才会将Bean数据加载到FactoryBean当中
注入方式 | 配置方式 |
---|---|
通过Bean的set方法注入 |
|
通过构造Bean的方法进行注入 |
|
ref时调用其他的bean当中的id,value调用的是普通的值,一般在setter当中利用property方法注入值,
注:property和constructor-arg的name属性和参数的名字必须要一样
public class UserDaoImpl implements UserDao {
//注入一个List集合
private List<String> list;
public void setList(List<String> list) {
this.list = list;
}
//注入一个字符串类型的Set集合
private Set<String> userSet;
public void setUserSet(Set<String> userSet) {
this.userSet = userSet;
}
//注入一个UserDao类的Set集合
private Set<UserDao> userDaoSet;
public void setUserDaoSet(Set<UserDao> userDaoSet) {
this.userDaoSet = userDaoSet;
}
//注入一个map集合
private Map<String,UserDao> map;
public void setMap(Map<String,UserDao> map) {
this.map = map;
}
//注入Properties
private Properties properties;
public void setProperties(Properties properties){
this.properties = properties;
}
public void show(){
System.out.println(list);
System.out.println(userSet);
System.out.println(userDaoSet);
System.out.println(map);
System.out.println(properties);
}
}
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd">
<bean id="userDao" class="com.huanglei.Dao.Impl.UserDaoImpl">
<property name="list">
<list>
<value>aaavalue>
<value>sssvalue>
list>
property>
<property name="userSet">
<set>
<value>asdsavalue>
<value>11111value>
<value>22222value>
<value>33333value>
set>
property>
<property name="userDaoSet">
<set>
<ref bean="userDao1">ref>
<ref bean="userDao2">ref>
<ref bean="userDao3">ref>
<ref bean="userDao4">ref>
set>
property>
<property name="map">
<map>
<entry key="m1" value-ref="userDao1">entry>
<entry key="m2" value-ref="userDao2">entry>
<entry key="m3" value-ref="userDao3">entry>
<entry key="m4" value-ref="userDao4">entry>
map>
property>
<property name="properties">
<props>
<prop key="p1">p111prop>
<prop key="p2">p222prop>
<prop key="p3">p333prop>
<prop key="p4">p444prop>
props>
property>
bean>
<bean id="userDao1" class="com.huanglei.Dao.Impl.UserDaoImpl">bean>
<bean id="userDao2" class="com.huanglei.Dao.Impl.UserDaoImpl">bean>
<bean id="userDao3" class="com.huanglei.Dao.Impl.UserDaoImpl">bean>
<bean id="userDao4" class="com.huanglei.Dao.Impl.UserDaoImpl">bean>
beans>
扩展:自动装配方式
如果被注入的属性类型是Bean引用的话,那么可以在
标签中使用autowire属性去配置自动注入方式,属
性值有两个:
byName
:通过属性名自动装配,即去匹配 setXxx 与id=“xxx” (name=“xxx”)是否一致;byType
:通过Bean的类型从容器中匹配,匹配出多个相同Bean类型时,报错。private UserService userService;
public void setUserService(UserService userService){
this.userService = userService;
}
<bean id="userDao" class="com.Smulll.Dao.Impl.UserDaoImpl" autowire="byName">bean>
<bean id="userService" class="com.Smulll.service.Impl.UserServiceImpl">bean>
Spring的xml标签大体上分为两类,一种是默认标签
,一种是自定义标签
标签
标签Spring的默认标签用到的是Spring的默认命名空间
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http: //www.w3.org/2001/xMschema-instance"
xsi:schemalocation="http: //www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
beans>
该命名空间约束下的默认标签如下:
标签 | 作用 |
---|---|
|
一般作为xml配置根标签,其他标签都是该标签的子标 |
|
Bean的配置标签,上面已经详解了,此处不再阐述 |
|
外部资源导入标签 |
|
指定Bean的别名标签,使用较少 |
标签,除了经常用的做为根标签外,还可以嵌套在根标签内,使用profile属性切换开发环境
<beans profile="test">
beans>
<beans profile="dev">
beans>
可以使用以下两种方式指定被激活的环境:
Dspring.profiles.active=test
System.setProperty("spring.profiles.active","test")
标签,用于导入其他配置文件,项目变大后,就会导致一个配置文件内容过多,可以将一个配置文件根据业务某块进行拆分,拆分后,最终通过
标签导入到一个主配置文件中,项目加载主配置文件就连同
导入的文件一并加载了
<import resource="classpath:UserModuleApplicationcontext.xml"/>
<import resource="classpath:ProductModuleApplicationcontext.xml"/>
<import resource="classpath:jdbc.properties"/>
标签是为某个Bean添加别名,与在
标签上使用name属性添加别名的方式一样,我们为UserServicelmpl指定四个别名: aaa、bbb、xxx、yyy
<bean id="userService" name="aaa,bbb" class="com.itheima.service.imp1.UserserviceImp1">
<property name="userDao" ref="userDao"/>
bean>
<alias name="userservice" alias="xxx"/>
<alias name="userservice" alias="yyy"/>
Spring的自定义标签需要引入外部的命名空间,并为外部的命名空间指定前缀,使用<前缀:标签>
形式的标签,称之为自定义标签,自定义标签的解析流程也是Spring xml扩展点方式之一
<bean id="userDao" class="com.itheima. dao .imp1.userDaoImpl" />
<context:property-placeholder/>
<mvc:annotation-driven/>
<dubbo:application name="application"/>
方法定义 | 返回值和参数 |
---|---|
Object getBean (String beanName) | 根据beanName从容器中获取Bean实例,要求容器中Bean唯一,返回值为Object,需要强转 |
T getBean (Class type) | 根据Class类型从容器中获取Bean实例,要求容器中Bean类型唯一,返回值为Class类型实例,无需强转 |
T getBean (String beanName,Class type) | 根据beanName从容器中获得Bean实例,返回值为Class类型实例,无需强转 |
//根据beanName获取容器中的Bean实例,需要手动强转
UserService userService = (UserService)applicationContext.getBean("userService");
//根据Bean类型去容器中匹配对应的Bean实例,如存在多个匹配Bean则报错
UserService userService2 = applicationContext.getBean(UserService.class);
//根据beanName获取容器中的Bean实例,指定Bean的Type类型
UserService userService3 = applicationContext.getBean("userService",UserService.class) ;