Spring学习6(6)基于java类,Groovy,编码的方式对Bean进行配置

Spring学习6(6)

基于Java类的配置

使用Java类提供Bean的定义信息

 普通的PoJo只要标注了@Configuration注解就可以为Spring容器提供Bean的定义信息,每个标注了@Bean的方法都相当于提供了一个Bean的定义信息代码如下:

package com.smart.conf;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConf{
	
	@Bean
	public UserDao userDao() {
		return new UserDao();
	}
	@Bean
	public LogDao logDao() {
		return new LogDao();
	}
	@Bean
	public LogonService logonService() {
		LogonService logonService = new LogonService();
		logonService.setLogDao(logDao());
		logonService.setUserDao(userDao());
		return logonService;
	}
}

 使用Bean的类型由方法返回值决定,名称默认和方法名相同,也可以通过入参显示的只当Bean的id如@Bean(name="userDao")@Bean所标注的方法提供了Bean的实例化逻辑。

 如果Bean在多个@Configuration配置类中定义,如何引用不同配置类中定义的Bean呢?例如UserDao和LogDao在DaoConfig中定义,logonService在ServiceConfig中定义:

package com.smart.conf;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class Daoconfig{
	@Bean
	public UserDao userDao() {
		return new UserDao();
	}
	public LogDao logDao(){
		return new LogDao();
	}
}

 需要知道的是@Configuration本身已经带有@Component了,所以其可以像普通Bean一样注入其他Bean中。ServiceConfig的代码如下:

package com.smart.conf;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;

@Configuration
public class ServiceConfig{
	
	@Autowired
	private Daoconfig daoConfig;
	
	@Bean
	public LogonService logonService() {
		LogonService logonService = new LogonService();
		
		logonService.setLogDao(daoConfig.logDao());
		logonService.setUserDao(daoConfig.userDao());
		return logonService;
	}
}

 不过这里不再是简单的使用类中定义的方法逻辑了,而是返回spring容器中相应的单例。当然我们也可以在@Bean上标注@Scope来定义范围如:

	@Scope("prototype")
	@Bean
	public UserDao userDao() {
		return new UserDao();
	}

 由于Spring容器自动对@Configuration的类进行改造(AOP增强)以植入spring容器对Bean的管理逻辑,所以使用java类配置需要将spring aop和CGLIB类包放入路径中。

使用基于Java类的配置信息启动spring容器

直接通过@Configuration类启动Spring容器

 Spring提供了一个AnnotationConfigApplicationContext类,可以直接使用标注了@Configuration的Java类。

public class JavaConfigTest{
	
	@Test
	public void javaconf1(){
		ApplicationContext ctx = new
					AnnotationConfigApplicationContext(AppConf.class);
		LogonService logonService = ctx.getBean(LogonService.class);
		logonService.printHello();
	}
}

 上述代码的意思是通过AnnotationConfigApplicationContext类的构造函数直接传入标注@Configuration的java类。其实其还可以通过编码的方式加载多个类,并且通过刷新容器应用这些配置类。

	
	@Test
	public void javaconf2() {
		AnnotationConfigApplicationContext ctx = new
				AnnotationConfigApplicationContext();
		ctx.register(Daoconfig.class);
		ctx.register(ServiceConfig.class);
		ctx.refresh();
		LogonService logonService = ctx.getBean(LogonService.class);
				logonService.printHello();
	}

 同样的,这种方法也可以合并配置类,在定义中加入@Import注解就可以在内存中合并多个配置文件。

通过XML配置文件

 因为标注了@Configuration@Component一样也是一个Bean,所以可以通过和注解相同的方法,利用<context:component-scan>的标签来扫描。

引用xml配置信息

 在使用java配置的方法时,还可以使用ImportResource来注入来自xml配置方法配置的bean。假设我们创建一个beans3.xml,其中代码如下:

	<bean id="userDao" class="com.smart.conf.UserDao"/>
		<bean id="logDao" class="com.smart.conf.LogDao"/>	

 而后在logon的配置文件中采用如下方式注入:

@Configuration
@ImportResource("classpath:com/smart/conf/Beans3.xml")
public class LogonAppConfig{
	
	@Bean
	@Autowired
	public LogonService logonService(UserDao userDao, LogDao logDao) {
		LogonService logonService = new LogonService();
		logonService.setUserDao(userDao);
		logonService.setLogDao(logDao);
		return logonService;
	}
}

基于Groovy DSL配置

使用Groovy DSL提供Bean定义信息

 Groovy DSL进行Bean定义配置时,类似于XML的配置,不过配置信息是通过Groovy来表达的。我们在resources.com.smart.groovy下创建spring-context.groovy

import com.smart.groovy.LogDao
import com.smart.groovy.LogonService
import com.smart.groovy.UserDao
import com.smart.groovy.XmlUserDao
import com.smart.groovy.DbUserDao
import org.springframework.core.io.ClassPathResource

beans{
	//1.声明context命名空间
	xmlns context:"http://www.springframework.org/schema/context"
	
	//2.与注解混合使用,定义注解Bean扫描包路径
	context.'component-scan'('base-package':"com.smart.groovy"){
	
		//3.排除不需要扫描的包路径
		'exclude-filter'('type':"aspectj", 'expression':"com.smart.xml.*")
	}
	//4.读取app-conf.properties配置文件
	def stream;
	def config = new Properties();
	try{
		stream = new ClassPathResource('conf/app-conf.properties').inputStream
		config.load(stream);
	}finally{
		if(stream!=null){
			stream.close()
		}
	}
	//5.配置无参构造函数Bean
	logDao(LogDao){
		bean->
			bean.scope="prototype"
			bean.initMethod="init"
			bean.destroyMethod="destory"
			bean.lazyInit=true
	}
	
	//6.根据条件注入Bean
	if("db"==config.get("dataProvider")){
		userDao(DbUserDao)
	}else{
		userDao(XmlUserDao)
	}
	
	//7.配置有参构造函数注入Bean,参数是userDao
	logonService(LogonService,userDao){
		logDao=ref("logDao")//8.配置属性注入,引用Groovy定义Bean
		mailService = ref("mailService")//9.配置属性注入,引用定义注解
	}
}

 这里注释4处是Groovy脚本加载资源配置文件。其中内容为:

dataProvider=db

 这句话在注释6中起到了选择要注入的Bean的作用。
 注释5是注入了一个无构造函数的Bean,其中logDao是定义的Bean的名称,括号内是类名。
 注释7定义了一个带有构造函数的Bean,括号内第一个参数是Bean的类名,第二个参数是构造参数,并且这里用ref的方法注入了属性。
 可以看到这里用到了很多Dao类,其中内容不是关键,这里将这些类的内容给出如下:

package com.smart.groovy;
import org.springframework.stereotype.Component;
public interface UserDao {
}
public class LogDao {
    private String dataProvider;
    public void saveLog(){}

    public void setDataProvider(String dataProvider) {
        this.dataProvider = dataProvider;
    }

    public void init(){
        System.out.println("initMethod....");
    }
    public void destory(){
        System.out.println("destoryMethod....");
    }
}
package com.smart.groovy;
public class XmlUserDao implements UserDao {
}
package com.smart.groovy;


public class LogonService {

    private UserDao userDao;
    private LogDao logDao;
    private MailService mailService;

    public LogonService(UserDao userDao) {
        this.userDao = userDao;
    }

    public void setLogDao(LogDao logDao) {
        this.logDao = logDao;
    }

    public void setMailService(MailService mailService) {
        this.mailService = mailService;
    }

    public MailService getMailService() {
        return mailService;
    }
}

package com.smart.groovy;

import org.springframework.stereotype.Component;

@Component
public class MailService {

}

启动spring容器

 spring为基于Groovy的配置提供了专门的ApplicationContext实现类:GenericGroovyApplicationContext。实例如下:

package com.smart.groovy;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.GenericGroovyApplicationContext;
import org.testng.annotations.Test;
import static org.testng.Assert.*;

public class GroovyTest{
	@Test
	public void getBeans() {
		//加载Groovy Bean配置文件
		ApplicationContext ctx = new GenericGroovyApplicationContext(""
				+ "classpath:com/smart/groovy/spring-context.groovy");
		
		//加载Bean
		LogonService logonService = ctx.getBean(LogonService.class);
		assertNotNull(logonService);
		
		//加载注解定义的Bean
		MailService mailService = ctx.getBean(MailService.class);
		assertNotNull(mailService);
		
		//判断是否注入DbUserDao
		UserDao userDao = ctx.getBean(UserDao.class);
		assertTrue(userDao instanceof DbUserDao);
	}
}

 这里test需要注意放上aspectj和Groovy的依赖。
 还需要注意这里的mailService是不需要的,在logonService就已经被注入了,这里只是为了测试一下注解。

通过编码方式动态添加Bean(此节暂时略过)

通过DefaultListableBeanFactory

 DefaultListableBeanFactory实现了ConfigurableListableBeanFactory接口,提供了可扩展配置等功能,可以通过此类实现Bean动态注入。
 为了保证动态注入的Bean也能被AOP增强,需要实现BeanFactoryPostProcessor接口,下面是一个实例:

package com.smart.dynamic;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.stereotype.Component;

@Component
public class UserServiceFactoryBean implements BeanFactoryPostProcessor{
	public void postProcessBeanFactory(ConfigurableListableBeanFactory bf)
		throws BeansException{
		
		//将ConfigurableListableBeanFactory转化为DefaultListableBeanFactory
		DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory)bf;
		
		//通过BeanDefinitionBuilder创建Bean定义
		BeanDefinitionBuilder beanDefinitionBuilder = 
				BeanDefinitionBuilder.genericBeanDefinition(UserService.class);
		//设置属性userDao,此属性引用已经定义的bean:userDao
		beanDefinitionBuilder.addPropertyReference("userDao","userDao");
		//可以注册一个Bean定义
		beanFactory.registerBeanDefinition("userService1", beanDefinitionBuilder.getRawBeanDefinition());
	
		//也可以直接注册一个Bean实例
		beanFactory.registerSingleton("userService2", new UserService());
		}
}

扩展自定义标签

 在开发产品级组件时,我们会将组件标签化,spring为第三方组件自定义标签提供了支持,需要经过下面几个步骤:

1. 采用XSD描述自定义标签元素属性

 第一步是定义标签元素的XML结构,采用XSD描述自定义标签的元素属性,我们在src.main.resources.com.smart.schema下创建userservice.xsd,代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns="http://www.smart.com/schema/service"
            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            xmlns:beans="http://www.springframework.org/schema/beans"
            targetNamespace="http://www.smart.com/schema/service"
            elementFormDefault="qualified"
            attributeFormDefault="unqualified">
    <xsd:import namespace="http://www.springframework.org/schema/beans"/>
    <xsd:element name="user-service">
        <xsd:complexType>
            <xsd:complexContent>
                <xsd:extension base="beans:identifiedType">
                    <xsd:attribute name="dao" type="xsd:string" use="required"/>
                </xsd:extension>
            </xsd:complexContent>
        </xsd:complexType>
    </xsd:element>

</xsd:schema>

 这里targetNamespace是指定一个自定义标签的命名空间,<xsd:element>是定义了一个user-service标签并在beans:identifiedType基础上定义了user-service标签的扩展属性“dao”。

编写Bean定义的解析器

 接下来编写用户服务标签解析类

public class UserServiceDefinitionParser implements BeanDefinitionParser {

    public BeanDefinition parse(Element element, ParserContext parserContext) {
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(UserService.class);
        //获取自定义标签属性
        String dao = element.getAttribute("dao");
        beanDefinitionBuilder.addPropertyReference("userDao",dao);
        AbstractBeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
        parserContext.registerBeanComponent(new BeanComponentDefinition( beanDefinition,"userService"));
        return null;
    }
}
注册自定义标签解析器

 上面一步完成了解析器的步骤,所以接下来要将解析器注册到spring命名空间

package com.smart.dynamic;

import org.springframework.beans.factory.xml.NamespaceHandlerSupport;

public class UserServiceNamespaceHandler extends NamespaceHandlerSupport {

    public void init() {
        registerBeanDefinitionParser("user-service", new UserServiceDefinitionParser());
    }
}

绑定命名空间解析器

 在resource.META-INF创建spring.handlers和spring.schemas两个文件,告诉自定义标签的文档结构以及解析它的类:
在spring.schemas文件下:
http\://www.smart.com/schema/service.xsd=com/smart/schema/userservice.xsd
在spring.handlers文件下:
http\://www.smart.com/schema/service=com.smart.dynamic.UserServiceNamespaceHandler

使用实例:

 我们可以在配置文件中使用user-service标签了,需要声明命名空间,代码如下:

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	   xmlns:us="http://www.smart.com/schema/service"
	   xsi:schemaLocation="http://www.springframework.org/schema/beans
          http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
          http://www.smart.com/schema/service http://www.smart.com/schema/service.xsd">
<bean id="userDao" class="com.smart.dynamic.UserDao" />
<us:user-service dao="userDao"/>
</beans>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值