Spring
文章目录
SSM
S:Spring:核心思想管理IOC组件,使用AOP思想完成功能增强
S:SpringMVC:接收浏览器发送的请求,并响应浏览器数据
M:Mybatis:封装JDBC,负责访问数据库,完成持久化对象
SSM:将各个框架整合,进一步了解各个功能的框架
IoC 控制反转
IoC容器负责管理大量对象的创建以及初始化(一般是service与dao),在IoC中,被管理的对象统一被称为Bean。在这个过程中,通过配置告知IoC容器,通过接口获取到IoC容器,获取到IoC容器后,通过接口方法获取bean对象,在pom.xml倒入坐标。
以小demo为例:
|--dao
---impl--BookDaoimpl
---BookDao
|--service
---impl--Bookserviceimpl
---Bookservice
操作步骤:
1.导入Spring坐标Spring-context(在pom.xml文件中写入依赖)
2.在项目文件夹内,右键-> New-> XML Configuration File-> Spring Config并给该文件起名字“applicationContext.xml”
3.进入applicationContext.xml”,配置bean,代码如下:
<beans>
<bean id ="bookdao" class="Demo.dao.impl.BookDaoimpl"/>
<bean id ="bookservice" class="Demo.service.impl.Bookserviceimpl"/>
</beans>
tips: bean标签化,ID是起名字(不固定),class为要实例化的对象类
4.获取IoC容器,新建一个Java主程序,代码如下:
public static void main(String[] args){
ApplicationContext ctx = new ClassPathApplicationContext("application.xml");
BookDao = (BookDao)ctx.getBean("bookDao");//这里引用的是ID
}
通过实例:通过在applicationContext中注册bean以及对象的bean信息即可,通过获取配置类的ID获取对象。
DI 依赖注入
在容器中bean与bean建立依赖关系的整个过程
操作实现:
1.去掉业务中new 对象的部分,仅保留声明的对象部分:(private BookDao bookdao,后面new部分不写)
2.提供对应的setter方法,传递参数使之能够传递到当前对象的方法
3.进入applicationContext.xml”中,在对应的BookDao 的内部标签中配置service与dao的关系:
<bean id ="bookdao" class="Demo.dao.impl.BookDaoimpl"/>
<bean id ="bookservice" class="Demo.service.impl.Bookserviceimpl">
<property name="bookDao" ref="bookdao"/>
</>
这里在自己的类中不去引用其他类的对象,property的配置就不需要写。
tips: property标签化,name表示关联的Java类文件中的对象属性名称(bookdao),ref属性代表参照的是配置文件中的哪一个bean的ID或者name(下节说到)
bean基础配置
可以为对象指定别名,在bean中使用name属性:
<bean id ="bookdao" name="dao dao2 " class="Demo.dao.impl.BookDaoimpl"/>
tips:使用多个别名用空格隔开
可以创建多个对象,在bean中使用scope属性,默认创建单例singleton,创建多个使用prototype
<bean id ="bookdao" name="dao dao2" scope="prototype" class="Demo.dao.impl.BookDaoimpl"/>
bean实例化
1.bean实例化–构造方法,在类中提供一个可以访问的无参构造方法,会调用
bean常规配置:
<bean id ="bookdao" class="Demo.dao.impl.BookDaoimpl"/>
2.bean实例化–静态工厂(这种方式为了兼容早期项目,装饰修改)
在原有项目的基础上,新建了类,里面有静态方法,用于返回创建的对象
public class BookDaoFActory {
public static Bookdao getBookDao(){
return new BookDaoimpl();
}
}
配置:
<bean id ="bookdao"
factory-method:"getBookDao"
class="Demo.dao.factory.BookDaoFactory"/>
tips:class所指定的类为创建对象,但是为了指定需要返回的方法对象类型,需要指定类里的方法指定返回的对象类型。
3.实例工厂与factory Bean
对象的的方法并非静态方法,而是实例方法,
配置:
<bean id ="userFectory"
class="Demo.dao.factory.userBookDaoFactory"/>
<bean id ="userDao"
factory-method="getUserDao" factory-bean="userFactory"/>
tips: 这里更为麻烦了一些,相当于对原来的bean进行了拆解。
工厂方法在原有的接口与实现方法上,又出来一个新的类–工厂类。用于返回对象
4.factoryBean实例化:*
新建一个userDaoFactoryBean类,并实现FactoryBean接口,泛型类型为自己想创建的对象返回类型,并实现这个接口的方法。
生命周期
1.在配置bean属性中有init-method属性,destroy-method属性,值分别为init,destroy
2.当然在java文件中使用接口也可以控制,继承initializingBean,DisposableBean
3.关闭容器的操作
ConfigurableApplicationContext
close()
registerShutDownHook()
依赖注入
setter注入:
简单类型(基本数据类型,string等)
引用类型(引用的对象)
构造器注入:
简单类型
引用类型
setter在之前已经说过,即:
在Java类中定义引用类型属性(private),并提供可以访问的set方法,然后在bean的property标签的ref属性注入引用的类型对象(在set的对象)
tips;其中文件中property的标签不是随便写的,一定要写在定义且设置set的配置bean中。
简单类型引用和引用一样,首先定义int或者String类型的数据,然后设置set方法引用,然后在该类文件的配置文件bean中进行property属性的定义,使用属性name绑定引用对象,然后使用value属性动态绑定对于属性的值。
构造器注入:*
首先,去掉set方法,换成传递参数的构造方法,然后在配置文件中的bean配置,
<bean id ="bookdao" class="Demo.dao.impl.BookDaoimpl"/>
<bean id ="bookservice" class="Demo.service.impl.Bookserviceimpl">
<constructor name="bookDao" ref="bookdao"/>
</bean>
tips:其中,配置构造器中的name是构造方法的形参。
为了解决简单引入注入问题中因为类中变量名称变化,而在配置中绑定失败的问题,在配置中,可以通过定向传递类型,与绑定索引位置方法进行参数绑定传递:
<bean id ="bookdao" class="Demo.dao.impl.BookDaoimpl"/>
<bean id ="bookservice" class="Demo.service.impl.Bookserviceimpl">
<constructor type=“java.lang.String” value="100"/>
</bean>
吐过构造参数中存在两个形参,第一个为String,第二个为int:
<bean id ="bookdao" class="Demo.dao.impl.BookDaoimpl"/>
<bean id ="bookservice" class="Demo.service.impl.Bookserviceimpl">
<constructor index="0" value="SSM"/>
<constructor index="1" value="100"/>
</bean>
bean依赖自动装配
Java中类文件保留set方法,即形参引用,在配置文件对对应的bean中自动参数进行配置即可:
<bean id ="bookdao" class="Demo.dao.impl.BookDaoimpl"/>
<bean id ="bookservice" class="Demo.service.impl.Bookserviceimpl" autowire="byName/byType"/>
tips:自动装配有几点需要注意:装配只用在引用类型中,并不能使用在简单类型中。第二个装配中,保证类型唯一,推荐使用ByType,而指定了名称,则使用ByName(不推荐),自动装配优先级低于set与构造器注入,同时存在则自动装配失效。
集合注入
简单类型的集合注入array,list,map,set,prosperty
在类中进行定义并存在set 方法:
private int[] array;
private Lsit<String> list;
private Set<String> set;
private Map<String, String> map;
private Prosperties pros;
public void setarray(int[] array){
this.array = array
} ...
在配置中需要这样进行分别配置:
<bean id ="bookdao" class="Demo.dao.impl.BookDaoimpl">
<property name="array">
<array>
<value> 100</value>
<value> 200</value>
<value> 300</value>
</array>
</property>
<property name="list">
<list>
<value> 100</value>
<value> 200</value>
<value> 300</value>
</list>
</property>
<property name="set">
<set>
<value> sre</value>
<value> str1</value>
<value> sav</value>
</set>
</property>
<property name="map">
<map>
<entry key="" value />
<entry key="" value />
<entry key="" value />
<entry key="" value />
</map>
</property>
<property name="pros">
<props>
<prop key="">value</prop>
<prop key="">value</prop>
<prop key="">value</prop>
</props>
</property>
</bean>
tips:注入集合,如果集合是引用类型(对象),则在后加入ref属性
<ref bean = "beanID">
第三方资源配置管理
Spring管理第三方资源,比如第三方连接池:DruidDataSource,CombePooledDataSource(C3P0),通过在配置文件的bean 设置ID与class(通常尝试去搜索或者猜测判断),进入类中查看方法注入(set or 构造),然后进行对应的注入方法(set-> property,构造方法-> constructor-arg),然后在property中使用name与value与类文件进行数据动态绑定。
加载properties配置信息
首先在与applicationContext.xml(同属于resource目录下)文件中存在一个jdbc.properties的配置文件,然后想要读取:
1.开辟新的命名空间,把第一行的bean换成context
xmlns:context.....\context
xsi:schemaLocation=
http:...context(beans位置替换)
2.使用context命名空间加载properties文件
<context:property-placeHolder location="property文件"/>
3.使用property加载读取属性值
<property name="username" value=${配置文件中的属性}/>
4.为了在某些情况下,配置文件的属性与系统文件的属性发生冲突,导致配置文件属性失效,为了有效解决这个问题,需要在命名空间加入system-properties-mode属性,值为NERVER,即系统属性不生效。
<context:property-placeHolder location="classpath:property文件" system-properties-mode=“NERVER”/>
5.为了加载多个配置文件,可以在多个location属性上的多个配置文件中间加逗号,当然可以使用通配符*.properties加载所有配置文件,当然,有时候第三方jar包也存在配置文件,这样要读取所有路径上的所有配置文件,需要这样写:classpath*😗.property文件
创建容器,可以加载多个配置文件,中间使用逗号隔开。
ApplicationContext ctx = new ClassPathXmlApplicationContext("application.xml",“xxxx.xml”);
获取bean
1.通过名称获取:
BookDao bookdao = (BookDao)ctx.getBean("bookDao");//这里引用的是ID
2.获取bean并指定类型
BookDao bookdao = (BookDao)ctx.getBean("bookDao", BookDao.class);//这里引用的是ID
3.通过类型进行获取
BookDao bookdao = (BookDao)ctx.getBean(BookDao.class);//这里引用的是ID
tips:按照类型查找的话,这个容器的类型只能有一个
注解开发
在类文件的类前通过注释@Component
代替之前的配置文件中标签的配置内容,当前,使用@Component("Dao")
可以直接在容器中直接通过名称进行创建,而使用@Component
则需要使用指定类型方法进行创建(上一节第三种bean的获取方式)。
核心文件需要在配置文件中通过组件扫描进行注册组件,在配置文件中这样进行配置,是在通过命名空间context进行实现的:
<context:component-scan base-package="com.packagename">
当然,为了能够更清晰的表现各个模块的注解,Spring提供了@Component的三个衍生注释,这三个注释的功能与@Component的功能完全一样,只是为了区分使用的场景:其中,@Controller 用于表现层bean的定义,@Service,用于业务层bean的定义,@Repository,用于数据层bean的定义
纯注解开发
之前我们使用application.xml的方式去获取容器,里面有很多代码,在开发中,为了简化开发,我们可以通过新建类并通过注释代替1.0版本的application.xml的配置文件加载方式,操作步骤如下:
1.新建一个类,在类之前通过加入@Configuration用于设置当前类为配置类,使用@Componentscan({1,2,…})进行组件扫描,括号内为组件扫描的包名
同时,在获取容器时使用注解配置类进行获取:
ApplicationContext ctx = new AnnotationConfigApplicationContext(类名.class);
注释开发bean的作用范围以及生命周期
在实现类前使用@Scope(“singleton”/“prototype”)决定创建对象是否相同,在组件层@Component的类中,通过自定义初始化与销毁的方法,在方法之前通过注释@postconstruct与@predestory来声明对象的周期方法
使用注解开发依赖注入
在@component及其衍生注释的类中,为了实现对象的注入,会使用自动装配(按类型)注入(@autowired),注意,这里是运用反射原理暴力反射为私有初始化,无需使用set方法(构造器也不用了,经典以前白学系列),装配指定的bean,需要使用@Qualifier(‘属性名’)(这个属性名,就是组件里面的括号名称,即1.0配置文件的ID),但是它不可单独进行使用,需要配合上述的@autowired进行使用
引用类型的注入如上,那么简单类型的注入呢,在属性名中,在声明变量前使用@Value()即可:
@Value("1000")
private String num
当然,这种形式是写死的,有时候,我们为了加载外部的properties文件,动态的读取,在注释开发应该怎么做呢?
首先,在配置类中,使用@PropertySource(“配置文件”),然后在@Value中使用
去
引
用
。
‘
@
V
a
l
u
e
(
"
{}去引用。 `@Value("
去引用。‘@Value("{name}")如果配置文件有多个,则应该加上数组,隔开写,同时,文件不支持通配符,即在1.0时候的*.properties的加载方式在纯注释里不再管用
@PropertySource({“配置文件1”,“配置文件2”,…})`
第三方bean的管理,之前的第三方数据库连接为例
使用@bean方法,定义一个方法即可,这个方法的返回值就是你定义的那个对象类型(dataSource),当然,我们不建议将这些代码直接写进配置类,而是新建一个类
public class JDBCconfig{
@bean
public DataSource datasource(){
DruidDataSource ds= new DruidDataSource()
...
return ds
}
}
然后在配置类中使用@import(JDBCconfig.class)进行导入即可,如果含有多个配置文件,则使用{}的形式进行依次添加。
当然,这样会存在一个问题,如果里面的数据缺少简单类型或者引用对象类型怎么办,简单类型这样做(成员变量)。
由于之前的是这样的:
@bean
public DataSource datasource(){
DruidDataSource ds= new DruidDataSource()
ds.setuser("user");
ds.setpassword("1234");
return ds
}
这样不利于数据的存储与写入,我们结合之前的property文件的读取方式还改进:
@Value("")
private String user;
@Value("")
private String password;
@bean
public DataSource datasource(){
DruidDataSource ds= new DruidDataSource()
ds.setuser(user);
ds.setpassword(password);
return ds
}
而引用类型这样做:
@Value("")
private String user;
@Value("")
private String password;
@bean
public DataSource datasource(BookDao bookdao){
DruidDataSource ds= new DruidDataSource()
ds.setuser(user);
ds.setpassword(password);
return ds
}
是的,直接加入形参即可,方法已经实现自动装配(是通过搜索组件component及其衍生组件的注册进行扫描查找得到进行的自动装配)(方法形参)
AOP
面向切面编程,在不动原始代码的基础上,进行功能增强,有点python装饰器的味道
核心概念:
连接点:程序执行过程中的任意位置
切入点:匹配连接点的式子
通知:在切入点执行的操作,一般是共性操作
通知类:定义通知的类
切面:描述通知与切入点的任意位置
简而言之,就是提取共性的模块,将模块加入到公共的方法中,使之共性的模块能够实现一样的功能。当然也可以实现不同的功能
AOP入门案例
1.导入aspect的包(aspectjweaver)
2.定义接口与实现类:
intelface BookDao(){
public void update();
public void save();
}
@component
public class BookDaoimpl implements BookDao{
public void save(){
共性功能
打印
}
public void update(){
共性功能
打印
}
}
3.新建一个通知类,在类中起一个方法,将共性功能写进去
public class Myadvice{
public void bdfore(){
共性功能
}
}
4.定义切入点。在哪个类的什么方法生效
定义一个私有方法,无参,无方法体,无返回值
public class Myadvice{
@Pointcut(execution("void com.package.dao.BookDao.update()"))
private void pt(){}
public void bdfore(){
共性功能
}
}
5.绑定切入点与通知之间的关系,在指定位置之前执行功能
public class Myadvice{
@Pointcut(execution("void com.package.dao.BookDao.update()"))
private void pt(){}
@before("pt()")
public void bdfore(){
共性功能
}
}
6.加载注释@component成为Spring能够识别的bean,添加@aspect让程序识别他是AOP
@Component
@Aspect
public class Myadvice{
@Pointcut(execution("void com.package.dao.BookDao.update()"))
private void pt(){}
@before("pt()")
public void bdfore(){
共性功能
}
}
7.开启配置对AOP的注释驱动支持@EnableAspectJAuoProxy
@Configuration
@ComponentScan("com.package")
@EnableAspectJAuoProxy
public class springconfig{
}
AOP核心:代理模式
切入点表达式
切入点表达式可以匹配接口类,也可以匹配实现类
可以使用通配符*进行匹配,任意参数使用…代替
AOP通知类型:
前置@before,后置@after 环绕@around,其中环绕比较重要
@Around("pt()")
public Object around(ProceedingJoinPoint pjb) throws Throwsable{
前
object s = pjb.proceed();
后
return s
}
如果原来的函数有返回值类型,则AOP的返回类型一定为Object,且加入形参ProceedingJoinPoint,没有返回值类型为void。
在环绕通知中,signature可以配合ProceedingJoinPoint,显示本次具体执行的是什么方法,什么类额度相关信息
AOP通知获取数据
获取切入点方法返回值
返回后通知(较少用),环绕通知(常用)
获取切入点的方法参数
JoinPoint(适用于前置,后置,返回后,抛出异常)
ProceedingJoinPoint(适用于环绕)
获取切入点的异常信息
抛出异常后通知
环绕通知
可以看到,环绕通知比较多,详细掌握它就可以了
AOP这个操作可以对原始的数据进行修改校验,并返回,相当于拦截器,非常实用。