Spring中的bean标签

bean标签

bean我们可以理解为一个Java类,在Spring中可以用bean标签来实现一个bean类,而不是在使用new这样的方法直接对代码内部进行修改了,这也叫做IOC,控制翻转,就是把对象的创建放在XML文档里,而不是在代码内部。

总结一下怎么使用bean标签,bean标签有2种方法,1种是在XML文件中进行配置,另一种是用注解来进行配置bean。我们先来具体的看看怎么使用XML文件配置

XML

在XML中我们通常这样来创建一个bean

  1. <bean id="demo01" class="com.zjl.spring.demo01"></bean>

这样的写法就可以简单的创建一个bean类的对象(或者说是准备工作?)那么怎么把bean加载到代码中呢?

  1. Applicationcontext application = new ClassPathApplicationContext(配置文件名.XML);
  2. demo01 d1 = application.getbean("demo01",demo01.class);

上面两句代码就可以帮助我们获取到其中的demo01的对象了。

当然还有值得一提的是如果采用上面的bean标签的话,是需要确保demo01类中是有无参构造器的,如果只有有参构造器,会报错的,下面我们就看看怎么使用有参构造器进行bean标签的创建呢?

  1. <bean id="demo01" class="com.zjl.spring.demo01">
  2. <constructor-arg name="jobset" ref="list01"></constructor-arg>
  3. <constructor-arg name="name" value="10部门"></constructor-arg>
  4. </bean>

我们可以通过在bean中插入constructor-arg标签进行数据的赋值。

那么如果有参构造器中出现了空值怎么进行赋值呢?

  1. <constructor-arg name="jobset" >
  2. <null></null>
  3. </constructor-arg>

那我们可以通过对构造器标签进行null标签的注入,这样就可以实现空值的注入。对于特殊字符的我们可以采用value标签中插入cdata来解决!

  1. <constructor-arg name="jobset" ><value><![CDATA[<周杰伦学Java>]]></value></constructor-arg>

谈到这里我觉得当我们创建一个类时,如果写了有参构造,还是最好把无参构造也写上。另外如果我们只是需要进行特定参数的赋值,可以采取setter方法进行属性的赋值,在XML中是使用property 和name与value属性进行赋值

  1. <property name="name" value="zhangsan"></property>

以上我们可以暂且归纳为基本数据类型的赋值,那么如果说出现了一个类中调用另一个自定义对象应如何进行赋值。废话少说,我们直接操作,第一步,新建2个bean,写好对应的id 和class 因为我们是一个属性那就直接用setter方法进行调用了!在我们要赋值的属性中,我们不再使用value进行赋值,而是采取ref,即引用,引用我们写好的bean标签中的id来实现赋值这一操作!

  1. <bean id="emp02" class="springxml.beans.emp"><property name="dept" ref="dept03"></property></bean>
  2. <bean id="dept03" class="springxml.beans.dept"></bean>

到这里我们已经完成了一个单一属性的赋值操作,当我们的类中如果出现了map list set 这要怎么处理?

首先我们要将xml的配置文件进行处理

  1. xmlns:util = "http://www.springframework.org/schema/util"
  2. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
  3. http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">

把上述配置更新到原来的xml头文件中。然后我们先来看怎么创建一个可以引用的集合类

  1. <util:list id="list02">
  2. <value>周杰伦</value>
  3. <ref bean="emp02"></ref>
  4. </util:list>
  5. <!--LIST的配置方法,其中value,我们可以认为是基本数据类型和String,如果想放入自定义数据类型就需要我们使用ref+bean.id注入- 下面是对于map的创建与使用,以及map中出现引用类型的处理方式-->
  6. <util:map id="map01">
  7. <entry key="周杰伦" value="李白"></entry>
  8. <entry key-ref="emp01" value-ref="dept"></entry>
  9. </util:map>
  10. <bean id="zzz" class="springxml.beans.dept"><property name="map" ref="map01"></property></bean>

至于array数组类型目前只能在property中进行加载

  1. <bean id="dept" class="springxml.beans.dept">
  2. <property name="name" value="开发部"></property>
  3. <property name="jobset">
  4. <array>
  5. <!--array可以换成set map 通用模版-->
  6. </array>
  7. </property>
  8. </bean>

到这里我们已经基本上完成了对bean标签及其属性的注入操作。下面我们聊聊bean中的其他属性

1.bean的作用域,通过加载bean来默认获取的对象是单例的,就是说我们无论getbean多少次都是一个bean对象,如果说我们想要获取多个bean对象在bean中插入属性scpe,设置属性为prototyope获取多个实例对象

  1. <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标签实现注入

  1. <!--byName自动装配-->
  2. <bean id="emp06" class="springxml.beans.emp" autowire="byName" ></bean>
  3. <bean id="dept" class="springxml.beans.dept">
  4. <property name="dname" value="财务部"></property>
  5. <property name="dno" value="123"></property>
  6. </bean>

bean的生命周期(多多少少有些问题)

bean的生命周期-(这里觉得不是很明白)

无参构造器 —> setter方法 —> bean标签中的初始化方法(init-method=”这里面写自己定义在类中的一个方法的名”)—> getbean —>bean标签中的销毁方法(destory-method=”这里面写自己定义在类中的一个方法的名”)

真正的销毁方法是applicationxontext.close( );

(destory-method是在销毁时执行,所以必须要进行bean的销毁,就是调用”真正的销毁方法“)

引入外部属性文件

提问:如何能够将properties文件加载到XML配置之中呢?

  1. <context:property-placeholder location="file:D:\Javacode\demo1\db.properties">
  2. </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个注解

  1. @Autowired 自动装配 ,这个注解可以声明在成员变量上,构造器上,set方法上,会根据属性的类型 自动完成属性的注入,我认为这个@Autowried方法如果想要自动装配,首先要确保这个属性对应的类型已经注册成了bean ,这是必须满足的前提,不然没法完成DI注入
  2. @Autowired 还有一个合作伙伴,因为单纯的靠类型进行自动装配是不完整的,如果有2个同类型属性注册到了bean中,我们是没法分辨的,所以就需要根据bean 的 id 来进行分辨了 。(仔细思考一下,应该是不会在一个系统中出现2个bean的,因为control,service 以及 dao 只有一个而已,哪里会有更多的对象呢)这个伙伴就是@Qualifier 这个注解用来辅助@Autowired ,如果说没有声明自动装配,@Qualifier是无法单独使用的。
  3. 另外一个就是@Resource 这个是可以单独使用的,如果不声明id ,就会按照类型去寻找,如果有id ,就会先去找id 相同的,找不到才会按照类型去装配,这个和@Autowired正好相反。
  4. @Value 这个注解用来完成一些基本数据类型以及字符串的依赖注入

作用域 scope 在spring中如果不声明scope那么就是默认的单例模式,用注解声明的话,那作用的方法自然是在类上,@Scope (“不同的作用域”) ,

AOP

1.代理模式

首先总结一下什么是代理模式,代理模式主要有4个角色。分别是 一个功能的接口,一个实现这个功能接口的实体类,一个代理这个实体类的代理类,以及一个客户端或者说测试函数

1.1 静态代理模式

静态代理是可以说代理类是写死的,我们一个代理类就指定代理一个相关的功能,然后去为不同的实体类去拓展功能,比如说生活中有很多业务,但是房屋中介只能从事房屋租赁这方面的工作,对于找工作就没法下手了。(因为代理类也是仅仅实现了一个功能的接口,所以只能通过去重写接口的方法,然后实际上调用的实体类的方法,这样可以在不破坏原有的封装性的前提下,完成对于功能的拓展,比如房屋中介的收租,签合同等等)

具体的代码和类

  1. //实体类
  2. public class host1 implements rent {
  3. private String name;
  4. public host1(String name) {
  5. this.name = name;
  6. }
  7. @Override
  8. public void rent() {
  9. System.out.println(name+" 我要租房子");
  10. }
  11. }
  12. //代理类
  13. public class proxy1 implements rent {
  14. private host1 host1;
  15. public proxy1(host1 h1) {
  16. this.host1 = h1;
  17. }
  18. @Override
  19. public void rent() {
  20. host1.rent();
  21. }
  22. }

所以说我们只需要在代理类中增加一些拓展工能就行了,也不需要实体类进行其他的改动!

1.2 动态代理模式

首先我们来看一下静态代理的缺点,静态代理的缺点在于,我们在进行代码编写的时候会很麻烦,因为一个代理类只有一种function,而且是写死的,代码量会很多(以至于一个功能要对应一个代理),所以我们要做一个综合性的代理模式,只要是要来办事的实体类,我们就可以给他代理,但是这个代理是怎么代理的呢,我们不去写死怎么去代理这个功能,我们要做的就是给实体类画大饼,让他进入中介,然后现场为他配备业务。

在Java中应该怎么写!Java中有一个接口代理的类这个类叫做Proxy。在这里先看代码

  1. public class allproxy implements InvocationHandler {
  2. Object func;
  3. public void setFunc(Object func){
  4. this.func = func;
  5. }
  6. public Object getProxy(){
  7. Object o = Proxy.newProxyInstance(this.getClass().getClassLoader(), func.getClass().getInterfaces(), this);
  8. return o;
  9. }
  10. @Override
  11. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  12. method.invoke(func,args);
  13. return null;
  14. }
  15. }

解读:首先我们继承了以InvocationHandler接口,然后这个接口是干什么的呢?注意看这个接口的invoke方法,这就是在相当于(以下拉言论都是我的猜测!)重新一个invoke方法,这部分得看底层 !!! 说一下怎么使用吧,我们首先创建一个实体类,然后把它传入setFunc中,接着使用getProxy 获取到代理对象,再调用对应的方法就可以了!

  1. public class client {
  2. public static void main(String[] args) {
  3. allproxy allproxy = new allproxy();
  4. host1 h1 = new host1("乔治");
  5. allproxy.setFunc(h1);
  6. rent proxy = (rent)allproxy.getProxy();
  7. proxy.rent();
  8. }
  9. }

2.AOP

所谓的AOP就是代理模式的一种体现,也是对于一种方法的增强,类似于合体或者解体,在不改变本体的前提下,进行功能的拓展。

AOP有2种实现方法,一种是调用Spring 的API接口。

这里我们直接说如何通知一个方法。AOP中主要有3种通知,1是前置通知,2是后置通知,3是环绕通知,这三种通知分别是下面的几种接口!

AfterReturningAdvice 返回结果后通知
MethodBeforeAdvice 方法前通知
MethodInterceptor 环绕通知

只要我们把要进行通知的方法写在继承了这几个接口的类中,就可以实现增强,不过需要进行XML文件的配置

准备环境

首先要在spring配置头文件中插入下面的语句

  1. xmlns:aop="http://www.springframework.org/schema/aop"
  2. http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd

这样我们就可以读取到的标签了。那么就可以进行aop的配置了,先看代码

  1. <bean id="service" class="com.zjl.service.serviceImpl"></bean>
  2. <bean id="before" class="com.zjl.service.before"></bean>
  3. <aop:config>
  4. <aop:pointcut id="service1" expression="execution(* com.zjl.service.serviceImpl.test(..))"/>
  5. <aop:advisor advice-ref="before" pointcut-ref="service1"></aop:advisor>
  6. </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 (在这里是前置或者后置通知都由我们实现的接口决定) 这里要强调一下怎么在测试类中实现通知

  1. service service = application.getBean("service", service.class);

因为动态代理的也是接口的原因,所以我们虽然从bean中得到的是实现类但是仍然需要以接口的形式来承接通知后的对象,进而调用通知后的方法。这里的环绕通知有一点点问题,不知道分界线在哪里!!!

2.自定义aop

这里的自定义aop的含义是我们不在借助spring中的接口了,而是通过自己去写一个类,在类中定义各种方法来实现我们想要拓展的功能

  1. <bean id="myaop" class="com.zjl.service.Myaop"></bean>
  2. <aop:config>
  3. <aop:aspect ref="myaop">
  4. <aop:pointcut id="service2" expression="execution(* com.zjl.service.service.test(..))"/>
  5. <aop:after method="after" pointcut-ref="service2"></aop:after>
  6. <aop:before method="before" pointcut-ref="service2"></aop:before>
  7. </aop:aspect>
  8. </aop:config>

这里myaop是自定义的增强方法的类,然后aspect中的ref 就是调用了这个类,再声明切入点和前置后置等等方法就可以完成这一配置

这个参数ProceedingJoinPoint可以用来当作环绕通知的分界线

3.注解实现aop

  1. <bean id="aop1" class="com.zjl.service.Myaop"></bean>
  2. <bean id="service" class="com.zjl.service.serviceImpl"></bean>
  3. <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
  1. @Aspect
  2. public class Myaop {
  3. @Before("execution(* com.zjl.service.service.test())")
  4. public void before(){
  5. System.out.println("before");
  6. }
  7. @Around("execution(* com.zjl.service.service.test())")
  8. public void after(ProceedingJoinPoint joinPoint) throws Throwable {
  9. System.out.println("after");
  10. joinPoint.proceed();
  11. System.out.println("aftter");
  12. }
  13. }