Spring中的bean标签
bean标签
bean我们可以理解为一个Java类,在Spring中可以用bean标签来实现一个bean类,而不是在使用new这样的方法直接对代码内部进行修改了,这也叫做IOC,控制翻转,就是把对象的创建放在XML文档里,而不是在代码内部。
总结一下怎么使用bean标签,bean标签有2种方法,1种是在XML文件中进行配置,另一种是用注解来进行配置bean。我们先来具体的看看怎么使用XML文件配置
XML
在XML中我们通常这样来创建一个bean
<bean id="demo01" class="com.zjl.spring.demo01"></bean>
这样的写法就可以简单的创建一个bean类的对象(或者说是准备工作?)那么怎么把bean加载到代码中呢?
Applicationcontext application = new ClassPathApplicationContext(配置文件名.XML);
demo01 d1 = application.getbean("demo01",demo01.class);
上面两句代码就可以帮助我们获取到其中的demo01的对象了。
当然还有值得一提的是如果采用上面的bean标签的话,是需要确保demo01类中是有无参构造器的,如果只有有参构造器,会报错的,下面我们就看看怎么使用有参构造器进行bean标签的创建呢?
<bean id="demo01" class="com.zjl.spring.demo01">
<constructor-arg name="jobset" ref="list01"></constructor-arg>
<constructor-arg name="name" value="10部门"></constructor-arg>
</bean>
我们可以通过在bean中插入constructor-arg标签进行数据的赋值。
那么如果有参构造器中出现了空值怎么进行赋值呢?
<constructor-arg name="jobset" >
<null></null>
</constructor-arg>
那我们可以通过对构造器标签进行null标签的注入,这样就可以实现空值的注入。对于特殊字符的我们可以采用value标签中插入cdata来解决!
<constructor-arg name="jobset" ><value><![CDATA[<周杰伦学Java>]]></value></constructor-arg>
谈到这里我觉得当我们创建一个类时,如果写了有参构造,还是最好把无参构造也写上。另外如果我们只是需要进行特定参数的赋值,可以采取setter方法进行属性的赋值,在XML中是使用property 和name与value属性进行赋值
<property name="name" value="zhangsan"></property>
以上我们可以暂且归纳为基本数据类型的赋值,那么如果说出现了一个类中调用另一个自定义对象应如何进行赋值。废话少说,我们直接操作,第一步,新建2个bean,写好对应的id 和class 因为我们是一个属性那就直接用setter方法进行调用了!在我们要赋值的属性中,我们不再使用value进行赋值,而是采取ref,即引用,引用我们写好的bean标签中的id来实现赋值这一操作!
<bean id="emp02" class="springxml.beans.emp"><property name="dept" ref="dept03"></property></bean>
<bean id="dept03" class="springxml.beans.dept"></bean>
到这里我们已经完成了一个单一属性的赋值操作,当我们的类中如果出现了map list set 这要怎么处理?
首先我们要将xml的配置文件进行处理
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 http://www.springframework.org/schema/util/spring-util.xsd">
把上述配置更新到原来的xml头文件中。然后我们先来看怎么创建一个可以引用的集合类
<util:list id="list02">
<value>周杰伦</value>
<ref bean="emp02"></ref>
</util:list>
<!--LIST的配置方法,其中value,我们可以认为是基本数据类型和String,如果想放入自定义数据类型就需要我们使用ref+bean.id注入- 下面是对于map的创建与使用,以及map中出现引用类型的处理方式-->
<util:map id="map01">
<entry key="周杰伦" value="李白"></entry>
<entry key-ref="emp01" value-ref="dept"></entry>
</util:map>
<bean id="zzz" class="springxml.beans.dept"><property name="map" ref="map01"></property></bean>
至于array数组类型目前只能在property中进行加载
<bean id="dept" class="springxml.beans.dept">
<property name="name" value="开发部"></property>
<property name="jobset">
<array>
<!--array可以换成set map 通用模版-->
</array>
</property>
</bean>
到这里我们已经基本上完成了对bean标签及其属性的注入操作。下面我们聊聊bean中的其他属性
1.bean的作用域,通过加载bean来默认获取的对象是单例的,就是说我们无论getbean多少次都是一个bean对象,如果说我们想要获取多个bean对象在bean中插入属性scpe,设置属性为prototyope获取多个实例对象
<bean id="dept01" class="springxml.beans.dept" scope="prototype"/>
如果说每次我们都要去bean内部设置属性有时候是有些麻烦的,Spring中提供了自动装配方法,我们通过设置属性autowire来设置是通过不那么,即通过bean.id来获取,还是bean.class来获取对应的形式,这里要注意的是如果有多个同一个class的bean byType会失效
tip:这题强调一下自动装配是怎么用的,对于byname,我们要确保bean类中的属性(成员变量)的名称合萼我们在xml配置文件中所对应的bean.id是相同的,这样才会识别出我们自动装配的bean,如果说是byType 就会寻找对应的成员变量类型的bean标签实现注入
<!--byName自动装配-->
<bean id="emp06" class="springxml.beans.emp" autowire="byName" ></bean>
<bean id="dept" class="springxml.beans.dept">
<property name="dname" value="财务部"></property>
<property name="dno" value="123"></property>
</bean>
bean的生命周期(多多少少有些问题)
bean的生命周期-(这里觉得不是很明白)
无参构造器 —> setter方法 —> bean标签中的初始化方法(init-method=”这里面写自己定义在类中的一个方法的名”)—> getbean —>bean标签中的销毁方法(destory-method=”这里面写自己定义在类中的一个方法的名”)
真正的销毁方法是applicationxontext.close( );
(destory-method是在销毁时执行,所以必须要进行bean的销毁,就是调用”真正的销毁方法“)
引入外部属性文件
提问:如何能够将properties文件加载到XML配置之中呢?
<context:property-placeholder location="file:D:\Javacode\demo1\db.properties">
</context:property-placeholder>
通过配置context标签就可以实现properties文件的注入,如何读取文件呢,在给属性赋值时我们只需要对value采用${key名称},就可以自动匹配到了!
Spring中的注解问题
注解就是用来解决IOC 和 DI 的问题,也不用在XML文件中写大量的配置
首先是注册bean的注解,一共有4个,但是这4个功能一样,只是为了便于在不同的层中使用,提升代码可读性,分别是
@Component pojo
@Controller 控制层
@Service 业务层
@Respository 持久层
声明位置是在类上,如果想起bean中类似的id ,可以在注解中设置value ,不写默认为首字母小写的类名
然后就是解决DI的问题了,解决DI问题,有4个注解
- @Autowired 自动装配 ,这个注解可以声明在成员变量上,构造器上,set方法上,会根据属性的类型 自动完成属性的注入,我认为这个@Autowried方法如果想要自动装配,首先要确保这个属性对应的类型已经注册成了bean ,这是必须满足的前提,不然没法完成DI注入
- @Autowired 还有一个合作伙伴,因为单纯的靠类型进行自动装配是不完整的,如果有2个同类型属性注册到了bean中,我们是没法分辨的,所以就需要根据bean 的 id 来进行分辨了 。
(仔细思考一下,应该是不会在一个系统中出现2个bean的,因为control,service 以及 dao 只有一个而已,哪里会有更多的对象呢)这个伙伴就是@Qualifier 这个注解用来辅助@Autowired ,如果说没有声明自动装配,@Qualifier是无法单独使用的。 - 另外一个就是@Resource 这个是可以单独使用的,如果不声明id ,就会按照类型去寻找,如果有id ,就会先去找id 相同的,找不到才会按照类型去装配,这个和@Autowired正好相反。
- @Value 这个注解用来完成一些基本数据类型以及字符串的依赖注入
作用域 scope 在spring中如果不声明scope那么就是默认的单例模式,用注解声明的话,那作用的方法自然是在类上,@Scope (“不同的作用域”) ,
AOP
1.代理模式
首先总结一下什么是代理模式,代理模式主要有4个角色。分别是 一个功能的接口,一个实现这个功能接口的实体类,一个代理这个实体类的代理类,以及一个客户端或者说测试函数
1.1 静态代理模式
静态代理是可以说代理类是写死的,我们一个代理类就指定代理一个相关的功能,然后去为不同的实体类去拓展功能,比如说生活中有很多业务,但是房屋中介只能从事房屋租赁这方面的工作,对于找工作就没法下手了。(因为代理类也是仅仅实现了一个功能的接口,所以只能通过去重写接口的方法,然后实际上调用的实体类的方法,这样可以在不破坏原有的封装性的前提下,完成对于功能的拓展,比如房屋中介的收租,签合同等等)
具体的代码和类
//实体类
public class host1 implements rent {
private String name;
public host1(String name) {
this.name = name;
}
@Override
public void rent() {
System.out.println(name+" 我要租房子");
}
}
//代理类
public class proxy1 implements rent {
private host1 host1;
public proxy1(host1 h1) {
this.host1 = h1;
}
@Override
public void rent() {
host1.rent();
}
}
所以说我们只需要在代理类中增加一些拓展工能就行了,也不需要实体类进行其他的改动!
1.2 动态代理模式
首先我们来看一下静态代理的缺点,静态代理的缺点在于,我们在进行代码编写的时候会很麻烦,因为一个代理类只有一种function,而且是写死的,代码量会很多(以至于一个功能要对应一个代理),所以我们要做一个综合性的代理模式,只要是要来办事的实体类,我们就可以给他代理,但是这个代理是怎么代理的呢,我们不去写死怎么去代理这个功能,我们要做的就是给实体类画大饼,让他进入中介,然后现场为他配备业务。
在Java中应该怎么写!Java中有一个接口代理的类这个类叫做Proxy。在这里先看代码
public class allproxy implements InvocationHandler {
Object func;
public void setFunc(Object func){
this.func = func;
}
public Object getProxy(){
Object o = Proxy.newProxyInstance(this.getClass().getClassLoader(), func.getClass().getInterfaces(), this);
return o;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
method.invoke(func,args);
return null;
}
}
解读:首先我们继承了以InvocationHandler接口,然后这个接口是干什么的呢?注意看这个接口的invoke方法,这就是在相当于(以下拉言论都是我的猜测!)重新一个invoke方法,这部分得看底层 !!! 说一下怎么使用吧,我们首先创建一个实体类,然后把它传入setFunc中,接着使用getProxy 获取到代理对象,再调用对应的方法就可以了!
public class client {
public static void main(String[] args) {
allproxy allproxy = new allproxy();
host1 h1 = new host1("乔治");
allproxy.setFunc(h1);
rent proxy = (rent)allproxy.getProxy();
proxy.rent();
}
}
2.AOP
所谓的AOP就是代理模式的一种体现,也是对于一种方法的增强,类似于合体或者解体,在不改变本体的前提下,进行功能的拓展。
AOP有2种实现方法,一种是调用Spring 的API接口。
这里我们直接说如何通知一个方法。AOP中主要有3种通知,1是前置通知,2是后置通知,3是环绕通知,这三种通知分别是下面的几种接口!
AfterReturningAdvice 返回结果后通知
MethodBeforeAdvice 方法前通知
MethodInterceptor 环绕通知
只要我们把要进行通知的方法写在继承了这几个接口的类中,就可以实现增强,不过需要进行XML文件的配置
准备环境
首先要在spring配置头文件中插入下面的语句
xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
这样我们就可以读取到的标签了。那么就可以进行aop的配置了,先看代码
<bean id="service" class="com.zjl.service.serviceImpl"></bean>
<bean id="before" class="com.zjl.service.before"></bean>
<aop:config>
<aop:pointcut id="service1" expression="execution(* com.zjl.service.serviceImpl.test(..))"/>
<aop:advisor advice-ref="before" pointcut-ref="service1"></aop:advisor>
</aop:config>
解读:首先我们将本体的bean声明出来,然后将要拓展的类的bean也声明出来。接着进行aop的配置。第一步声明aop:config ,然后声明切入点(aop:pointcut),即准确的定为到本体的位置 ,后面的expression表达式的含义是execution( )中第一个*代表的是方法的返回值,com.zjl.service.service.Impl.test 到这里是定位到指定的包,下面的类,下面的一个方法,最后(..)代表的是方法中的参数,写上2个 . 的含义是任意参数。这样我们就可以告知aop对这个方法进行通知,接下来的aop:advisor 就是为切入点增添功能,advice-ref后面接着的是我们已经实现了通知接口的bean id 后面的pointcut-ref是告知对应的切入点的id (在这里是前置或者后置通知都由我们实现的接口决定) 这里要强调一下怎么在测试类中实现通知
service service = application.getBean("service", service.class);
因为动态代理的也是接口的原因,所以我们虽然从bean中得到的是实现类但是仍然需要以接口的形式来承接通知后的对象,进而调用通知后的方法。这里的环绕通知有一点点问题,不知道分界线在哪里!!!
2.自定义aop
这里的自定义aop的含义是我们不在借助spring中的接口了,而是通过自己去写一个类,在类中定义各种方法来实现我们想要拓展的功能
<bean id="myaop" class="com.zjl.service.Myaop"></bean>
<aop:config>
<aop:aspect ref="myaop">
<aop:pointcut id="service2" expression="execution(* com.zjl.service.service.test(..))"/>
<aop:after method="after" pointcut-ref="service2"></aop:after>
<aop:before method="before" pointcut-ref="service2"></aop:before>
</aop:aspect>
</aop:config>
这里myaop是自定义的增强方法的类,然后aspect中的ref 就是调用了这个类,再声明切入点和前置后置等等方法就可以完成这一配置
这个参数ProceedingJoinPoint可以用来当作环绕通知的分界线
3.注解实现aop
<bean id="aop1" class="com.zjl.service.Myaop"></bean>
<bean id="service" class="com.zjl.service.serviceImpl"></bean>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
@Aspect
public class Myaop {
@Before("execution(* com.zjl.service.service.test())")
public void before(){
System.out.println("before");
}
@Around("execution(* com.zjl.service.service.test())")
public void after(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("after");
joinPoint.proceed();
System.out.println("aftter");
}
}