why spring
1.可解决大量的重复创建对象的过程,将对象的创建交给spring。
2.创建对象方式:
1.直接new
2.通过反射 Class class = Class.forName("com.xxx.xxx")
class.newInstance().var
spring 工厂模式
1.工厂模式出现原因:解耦合,由工厂帮助创建对象
2.原理:
-
配置applicationContext.xml文件,在文件当中定义需要被工厂进行管理的bean。
-
创建对象 ApplicationContext
ApplicationContext:接口(本质)
对应的实现类:1.非web环境下(main/junit):classPathXmlApplicationContext
2.web环境下:XmlWebApplicationContext
线程安全,可以多线程并发访问
获取有需要使用的bean对象
相关方法:
getBean()
方式一:直接通过id
UserService userServiceImpl = (UserService) applicationContext.getBean("userService");
方式二:指定id和类型,避免强制转换
UserService userService1 = applicationContext.getBean("userService", UserService.class);
方式三:只通过类型,该类型必须只有一个bean
UserService userService2 = applicationContext.getBean(UserService.class);
getBeanDefinitionNames()
获取xml定义的bean的id数组
String[] names = applicationContext.getBeanDefinitionNames();
getBeanNamesForType()
根据类型获得bean的id数组
String[] names = applicationContext.getBeanNamesForType(UserService.class);
containsBeanDefinition()
是否包含bean的定义,只能判断id,不能判断别名name
boolean userService = applicationContext.containsBeanDefinition("userService");
3.基础实现原理:
通过配置文件获得对象的class文件,再反射调用无参构造进行创建,私有的无参构造也可以。
ps:实体类对象不用spring创建,因为需要数据--->持久层创建
spring注入
1.概念:通过spring工厂及配置文件,对bean对象的成员属性进行赋值。
2.价值:一般赋值方式:get/set ---->存在耦合 注入:解耦合,但还是需要get.set方法
3.方式:
<bean id="student" class="com.boss.leraning.springdemo.bean.Student">
<property name="age" value="18"></property>
</bean>
4.实现原理:
调用get set 方法:对应着property里面的name 调用setName方法---------->所以也叫set注入
set注入
注入各种类型的成员变量方法
1.注入的成员变量的类型多种,不单只有int 和string ------->用value赋值即可
2.JDK提供的数据类型
-
8种基本数据类型+String
<value>xxxx</value>
-
数组类型
<list> <value> aa</value> </list>
-
set集合
<set> <value></value>//不一定是value,只有set的泛型是int/string <ref bean=""/>//自定义类型 </set>
-
list集合
<list> <value></value>//不一定是value,只有set的泛型是int/string <ref bean=""/>//自定义类型 </list>
-
map集合//键遍历,值遍历,键值遍历
<map> <entry> <key>//键 <value> </value>//键的值 </key> <value></value>//值,不一定是value,看具体情况 </entry> </map>
-
properties
<props> <prop key="">value</prop>//value只能是字符串类型 </props>
3.用户自定义类型
* 一个property标签调用一次set方式
方式一:直接在bean内部定义另一个bean
<bean id="useService "class=" ">
<property name="useDao">//调用set
<bean class="userdao"></bean>/创建对象
</property>
</bean>
//方式二:当userDao需要被别的对象使用
方式一问题:多次创建userDao浪费内存
<bean id="userDao" class=""></bean>
<bean id = "userService">
<property name="useDao">
<ref bean="userDao"/>
</property>
</bean>
//简写方式:
<bean id ="" class="">
<property name="" value=""></property>
<property name="" ref=""></property>
</bean>
构造注入
1.通过有参构造方法对成员变量进行赋值--->定义有参构造函数
2.方式
<bean id="",class="">
<constructor-arg>//一个该标签调用一次构造方法,传递一次参数
<value></value>
</constructor-arg>
</bean>
3.构造参数重载
通过控制constructor标签的数量,找到对应的构造函数
对于参数个数相同情况,要通过类型去区分,eg ,name和age
否则会按顺序去构造
<bean id="",class="">
<constructor-arg type="int" ,value=""></constructor-arg>
</bean>
总结
1.一般多数采用SET注入
反转控制(ioc inverse of Control)
1.控制的概念:对成员变量赋值的控制权-----代码/配置文件+Spring工厂
2.反转控制:将控制权从代码转移到配置文件当中。
依赖注入(dependency injection)
1.依赖:使用到它,需要它----->将它作为成员变量,通过配置文件进行赋值注入
复杂对象
创建复杂对象的方式
-
FactoryBean接口:
1.实现接口
public class MyFactoryBean implments FactoryBean<欲创建的对象的泛型>() { //待重写的方法 public Object getObject(){ //用于书写创建复杂对象的代码,将其作为返回值返回 } public class getObjectType(){ //返回所创建对象的class对象 } public boolean isSingleton(){ //返回创建对象是否是单例模式 //false,每次回创建新的复杂对象 } }
2.配置文件的配置
<bean id="conn" class="MyFactoryBean"></bean>
//通过getBean("conn")获取到的不是MyFactoryBean,而是它所创建的复杂对象---connection
//通过getBean("&conn")获取到是MyFactoryBean
3.依赖注入例子:
4.实现原理:1.id+class---->获得ConectionFactoryBean----->通过getObject获得conn对象。
-
实例工厂
1.使用原因:(1)spring框架侵入避免:离开了spring无法进行获得对象。
(2)无法拿到ConnectFactoryBean的.java文件
2.开发步骤:
(1)编写ConnectionFacctory---->编写获得conn对象的代码
(2)编写配置文件,让spring管理它
<bean id="connectionFactory" class=""></bean>//配置了实例 <bean id="conn" factory-bean="connectionFactory" factory-method="getConnection"></bean>
-
静态工厂
与实例工厂的区别就是工厂的方法是静态的。
控制对象的创建次数
1.复杂对象:isSingleton方法返回true/false
2.简单对象:通过在bean注入的时候编写scope属性:singleton(默认值)/prototype
创建一次的对象:
1.sqlSessionFactory
2.Dao
3.UserService
需要创建多次的对象:
1.sqlSession
2.Connection
对象的生命周期
对象的各个阶段
- 创建阶段
1.对于singleton对象
在工厂创建的时候就进行创建
懒初始化:在bean标签当中加入属性lazy-inti="true",也能达到prototype的效果
2.prototype对象
在对象被获取调用的时候,才被创建
-
初始化阶段
1.什么是初始化? 数据库,IO,网络流等资源操作。应用一般比较少。 由程序员编写初始化方法,经由spring进行调用进行初始化 形式一: 实现接口initializingBean接口当中的public void afterPropertiesSet()方法 形式二: 编写初始化方法,在<bean id="" class="" ,init-method="初始化方法名"/> //创建对象-->注入-->初始化
-
销毁阶段
工厂关闭时,spring调用对象的销毁方法。 ctx.close; 销毁操作:释放资源的操作。 //方式一: 实现DisposableBean当中的public void destory()方法,该方法存在异常,可抛出 //方式二: 销毁方法由程序员进行编写,在<bean id="",class="" destory-method="销毁方法名"> 注意: 销毁操作只对sigleton对象有用。
配置文件参数化
1.参数化的目地:能够方便后期的维护,将一些经常修改的值,定义到一个property文件当中。通过${kay名}获得值。
例如:
1.新建小配置文件:
2.配置文件的内容:
3.将小配置文件整合spring配置文件
classpath:resouce 和同级的java文件夹下的内容的整合。
自定义类型转换器
1.在注入的时候将String转换为需要注入的属性的类型。
2.在某些情况下Spring定义的类型转换器不满足我们的需求,例如将string类型转化为Date 类型。
内置只支持---”yyyy/MM/dd"的格式
-----------》进行自定义类型转换器
-
方式:
1.实现converter接口
//泛型当中的第一个表示原始类型,第二个表示要转化类型 public class MyConverter implements Converter<String,Date>(){ @override public Date converter(String source)//source就是配置文件中代表日期的字符串 { SimpleDateFormat sdf = new SimpleDateFormatImpl("yyyy-MM-dd");//存在异常,try-catch Date date= sdf.parse(source); return date;//转化后的返回值会被spring返回给待注入的对象--->接口回调 } }
2.添加到配置文件
spring提供了一个类ConversiobServiceFactoryBean,将自己写的转换器赋值给这个类的属性converters
该属性是set集合类型。该类命名中的id必须为conversionService
后置处理Bean
BeanPostProcessor(接口):对工厂创建的对象进行进一步的再加工
待重写方法:
Object postProcessBeforeInitiallization(Object bean String BeanName){
//在创建完对象,待初始化之前进行调用
return bean;//返回加工之后的对象
}
Object postProcessAfterInitiallization(Object bean String BeanName){
//在初始化完成之后进行调用
return bean;//返回加工之后的对象
}
//如果没有实现初始化操作的化,两个方法没有区别,但是不管有没有进行加工都要将bean对象进行返回。
-
开发步骤:
1.实现BeanPostProcessor
2.配置文件
<bean id ="" class=""></bean>
注意 beanPostProcessor会对工厂的所有对象都进行加工,所以前面代码要进行类型判断,instanceof()
代理设计模式
1.javaEE的开发层次当中最重要的是service层
dao--->service--->controller
2.service的功能
性能:业务的开始时间和结束时间之间的差。
日志:谁,何时,做何事
-
service层到底需不需要写额外功能?
调用者:需要,事务等处理功能。
软件设计者:不需要,避免代码入侵。
- 现实生活中例子
1.代理设计模式概念:通过代理类(proxy)来完成目标类的额外功能,利于目标类的维护。改动额外功能时不用改变目标类
2.
- 与目标类相同的名字
- 实现额外功能
- 有目标类的接口实现
以上代理方法为静态代理为每一个原始类都编写一个代理。
-
静态代理缺点:
1.代理类数量过多,不利于项目管理。
2.额外功能维护性差。如果想改变日志的格式,就得所有的代理类都修改。
spring 动态代理
本质与静态代理相同
1.导入相关的jar包
-
开发步骤:
1.创建原始对象
编写原始对象---userService 创建对象--->spring的bean注入
2.额外功能
实现接口MethodBeforeAdvice中的Before方法 public class before implements MethodBeforeAdvice{ @override public void bofore(Method method Object[] args Object target ) //method:原始类中要增加额外功能的方法 //args:原始方法的参数 //target:额外功能所增加给的对象 } 在配置中进行bean注入 <bean id="before" class="xxxx.MyMethodBeforeAdvice"></bean>
3.定义切入点
切入点:定义额外功能加入的位置。
//通过定义切入点来指定你的Before方法需要加给那些方法 * 通过配置文件进行定义: <aop:config> <aop:pointcut id=“pc” expression=“excution(* *(..))”/>//所以的方法都作为切入点 </aop:config>
4.组装
<aop:config> <aop:pointcut id=“pc” expression=“excution(* *(..))”/>//所以的方法都作为切入点 <aop:advisor advice-ref="before" pointcut-ref="pc">//组装,把切入点与before方法进行整合 </aop:config>
5.使用
通过getBean(原始对象)就可以获得代理对象--->要用接口类型进行存储 ApplicationContext ctx = new ClassPathXmlApplicationContext(); UserService userService = (UserServiece)ctx.getBean();
-
spring创建的动态代理类在哪里?
spring通过动态字节码技术在jvm里创建,在jvm使用结束后也消失。
-
-
好处:
1.简化代理的操作,额外功能只写一个额外功能类即可,然后加到方法前面
2.提升代理类的维护性---->打开扩展,关闭修改。
不想要这个额外功能,写个新的去绑定配置即可,省去旧的修改。
方式二(推荐,可以在原始方法的前后都添加额外功能):
实现接口MethodInterceptor中的invoke方法
pucblic class Arround implement MethodInterceptor{
@override
//该方法的参数就是所要添加额外功能的原始方法
public Object invoke(MethodInvocation invocation) Throws Throwsable{
sout"------"//按自己要求添加额外功能
Object ret= invocation.proceed();//表示让原始方法运行,以便让额外功能添加在其之后
return ret;
}
}
//后步骤与MethodBeforeAdvice相同
该接口的功能比MethodBeforeAdvice更加强大,并且可以通过改变ret,改变返回值。
-
何时在原始方法前后都添加额外功能?
事务 :原始方法执行前开启事务,执行之后提交事务
异常:在原始方法抛出异常的时候执行
切入点详解
方法切入点
切入点的位置:决定额外功能加入的位置
<aop:pointcut id="pc" expression="execution(* *(..))"></aop:pointcut>
//切入点函数:execution()
//切入点表达式:execution(* *(..))
第一个* --->修饰符,返回值
第二个*---->方法名
()中的..---->参数个数任意
eg.
* login(String)//方法login,参数一个,如果是非java.lang包中类型写全限定名
* login(String,..)//表示只要第一个参数是String即可,后参数的个数和类型没要求
-
精准的方法切入点的限定
在不同的包下的方法可能完全相同
解决方法:
类切入点
1.使用场景:对于某个类中的所有方法加入额外功能。
2.方式:
//所有包中的userServiceImpl类中的所有方法
<aop:pointcut id="pc" expression="execution(* *..uerServiceImpl.*(..))"></aop:pointcut>
包切入点
定义在某个包下的所有的类的切入点:
语法:
//该包中的所有类的所有的方法
<aop:pointcut id="pc" expression="execution(* com.xxx.xxxx.*.*(..))"></aop:pointcut>
//该包及其子包下的所有类的所有方法,包后多加一个点
<aop:pointcut id="pc" expression="execution(com.xxx.xxxx..*.*(..))></aop:pointcut>
//更具有实战价值
切入点函数
1.作用:执行切入点表达式
2.常用的切入点函数:
execution:功能最为齐全,但是表达式书写麻烦
----->其他的函数进行简化,但是本质还是execution
* args(String String)//关心函数的参数。有2个String参数的都满足
* within(*..userSericeImpl)//用于类或包的切入点,表示所有的UserService这个类
within(com.xxx.xxx..*)//包及其子包下的所有的类原表达式:execution(* com.xxx.xxx..*.*(..))
* annotation(com.xxx.xxx.log)//具有特殊注解的方法,例如自定义注解log,内部写注解全限名
切入点函数逻辑运算
1.概念:讲多个切入点函数之间进行and或者or运算以满足更为复杂的需求。
and
案例:login 同时参数为2个String
execution(* login(..)) and args(String String)
注意:与操作不能用于同种类型的切入点函数,不能execution and execution
案例: login 和 register----->用and结果为空 ,应该用or操作
or
案例: login 和 register----->用and结果为空 ,应该用or操作
execution(* login(..)) or execution(* register(..))
AOP编程
AOP概念
本质就是Spring的动态代理,通过代理类为原始类增加额外功能。
好处:利于原始对象的维护。
AOP开发步骤
1.原始对象
2.额外功能
3.切入点
4.组装
切面 :由具有相同性质的点所共同构成的面。
AOP底层实现原理
待思考的问题:
1.aop如何帮我们创建动态代理对象?
2.为何通过id值获得的是代理对象而不是原始对象?
JDK动态代理
1.复习代理创建的三要素
- 原始对象
- 额外功能
- 代理对象与原始对象实现相同的接口
2.Proxy.NewProxyInstance()方法参数的分析:
3.具体编码实现
public calss TestJDKProxy(){
public static void main(String[] args)
{
//创建原始对象
private UserService userService = new UserService();
//通过jdk生产代理对象
//通过匿名内部类实现接口
InvocationHandler handler = new InvocationHandler(){
@override
public Object invoke(Object proxy , Mehtod method , Object[] args) Throws Throwable{
//额外功能
sout."-----Proxy-log-------"
//让原始方法运行
Object ret=method.invoke(userService,args);
retuen ret;
}
}
UserService userServiceProxy = (UserService)
Proxy.NewProxyInstance(TestJDKProxy.class.getClassloader,userService.getCalss.getInterface(),handler)//强制转换获得UserService
}
}
cglib动态代理
1.适用情形:原始类没有实现接口,只一个原始类
2.解决方法:继承原始类
3.编码步骤:
public class TestCglib(){
public static void main(String [] args){
//创建原始对象
private UserService userService;
//Cglib包自带的方法
Enhancer.SetClassLoader(TestCglib.class.getClassLoader())//设置创建代理对象时的类加载器,也是借的
Enhancer.SetSuperClass(userService.getClass())//设置父类的class文件,在jdk当中使用的是接口,这里是直接目标类
//用内部类实现MethodInterceptor接口,与spring当中的接口不是一个,所处的包位置不同
MehodInterceptor interceptor = new MethodInterceptor(){
@override
public Object interceptor(Object o, Method method,Object[] args,MethodProxy methodProxy) Throws Throwable{
sout."----cglib----log----"//实现额外功能
Object ret =method.invoke(userService,args);//执行原始类的方法
//方法执行的三要素:1.执行对象--->原始对象
// 2.执行的方法 3.执行方法的参数
return ret;
}
}
Enhancer.SetCallBack(interceptor)//通过实现接口的内部类设置额外功能;
UserService userServiceProxy = (UserService) Enhancer.create();
//之后就可以使用代理对象userServiceProxy了
}
}
spring如何创建代理对象
1.aop如何帮我们创建动态代理对象?
2.为何通过id值获得的是代理对象而不是原始对象?
3.具体编码
(1)编写原始对象,并配置bean
(2)实现BeanPostProcessor这个接口,并将实现类配置bean
基于注解的AOP编程
1.基本步骤
- 原始对象
- 额外功能
- 切入点
- 组装切面
区别:通过切面类定义额外功能和切入点
@Aspect
public class MyAspect {
@Around("execution(* *.*(..))")//表示所有方法
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("---------aspect----log-------");
Object ret = joinPoint.proceed();
return ret;
}
}
在配置文件当中进行配置
<bean id="userService" class="UserServiceImpl"/>
<bean id="myAspect" class="MyAspect"/>
<!-- 开启aop注解编程,设置底层为cglib将proxy-target-class设置为true-->
<aop:aspectj-autoproxy proxy-target-class="true"/>
细节:切入点复用
@Aspect
public class MyAspect {
//切入点的复用,避免对于各种around方法当中的切入点进行多次修改
//eg.将所有的login要该成register就要2个around函数都修改
@Pointcut("execution(* *.login(..))")
public void myPointCut(){}//空的函数体
@Around("myPointCut()")//直接写自定义的切入点函数名即可
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("---------aspect1----log-------");
Object ret = joinPoint.proceed();
return ret;
}
@Around("myPointCut()")
public Object around2(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("---------aspect2----log-------");
Object ret = joinPoint.proceed();
return ret;
}
}
开发的时候可能存在的问题: