java 自定义注解 实例化_基于Spring Schema的自定义注解示例

简述

本教程主要介绍如何扩展Spring的xml配置,让Spring能够识别我们自定义的Schema和Annotation。

这里我们要实现的功能如下,首先让Spring能够识别下面的配置。

这个配置的要实现的功能是,配置完后能够让 Spring 扫描我们自定义的@Endpoint注解。

创建项目

首先需要创建一个Java项目,这里使用Maven创建一个quickstart项目(普通Java项目)。

xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">

4.0.0

spring-schema

com.system.schema

1.0-SNAPSHOT

1.6

UTF-8

org.springframework

spring-beans

5.0.2.RELEASE

org.springframework

spring-context

5.0.2.RELEASE

junit

junit

4.12

test

org.apache.maven.plugins

maven-compiler-plugin

1.6

1.6

定义Schema

xmlns:xsd="https://www.w3.org/2001/XMLSchema"

xmlns:beans="https://www.springframework.org/schema/beans"

targetNamespace="https://www.orchome.com/schema/std/ws"

elementFormDefault="qualified"

attributeFormDefault="unqualified">

关于Sechma的知识此处不在介绍,不会用的小伙伴们可以看这篇文章:https://www.orchome.com/758 ,sechma位置在src/main/resources/META-INF/schema/stdws-1.0.xsd。

定义注解

package com.system.annotation.custom;

import java.lang.annotation.Documented;

import java.lang.annotation.ElementType;

import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

import java.lang.annotation.Target;

/**

* 用于暴露服务,通过在类上加入{@code@Endpoint}注解实现服务暴露的目的。

* 扩展Spring的Bean扫描功能,在Bean上加入此注解后会自动注册到Spring容器中。

*/

@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

@Documented

public @interface Endpoint{

/**

* 此Endpoint在Spring容器中的ID

*@return

*/

String id();

/**

* 服务发布的地址,服务器地址及端口号和项目路径

*@return

*/

String address();

}

在Spring中的配置

在Spring中加入命名空间,并使用标签,如下。这里要用到Spring的注解扫描功能。文件:src/main/resources/SpringAnnotaionContext.xml。

xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"

xmlns:std="https://www.orchome.com/schema/std/ws"

xsi:schemaLocation="https://www.springframework.org/schema/beans

https://www.springframework.org/schema/beans/spring-beans-4.0.xsd

https://www.orchome.com/schema/std/ws

https://www.orchome.com/schema/std/ws/stdws-1.0.xsd">

在配置中定义了要扫描的包,不依赖与context的配置。

命名空间支持

要实现命名空间支持,需要继承自NamespaceHandlerSupport。

package com.system.annotation;

import com.system.annotation.process.EndpointBeanProcessor;

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

public class AnnotationNamespaceHandler extends NamespaceHandlerSupport{

public void init() {

this.registerBeanDefinitionParser("annotation-endpoint", new AnnotationBeanDefinitionParser(EndpointBeanProcessor.class));

}

}registerBeanDefinitionParser 方法将配置支持添加到Spring中。

annotation-endpoint是配置支持的元素。

AnnotationBeanDefinitionParser是处理配置的类。

EndpointBeanProcessor是处理 @Endpoint 注解的Bean的类,后面会有详细的讲述。

处理配置

需要实现BeanDefinitionParser

package com.system.annotation;

import org.springframework.beans.factory.config.BeanDefinition;

import org.springframework.beans.factory.support.RootBeanDefinition;

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

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

import org.springframework.util.StringUtils;

import org.w3c.dom.Element;

public class AnnotationBeanDefinitionParser implements BeanDefinitionParser{

private final Class> beanClass;

public AnnotationBeanDefinitionParser(Class> beanClass) {

this.beanClass = beanClass;

}

public BeanDefinition parse(Element element, ParserContext parserContext) {

RootBeanDefinition beanDefinition = new RootBeanDefinition();

beanDefinition.setBeanClass(beanClass);

beanDefinition.setLazyInit(false);

String id = element.getAttribute("id");

if(id == null || id.length() == 0 ){

String name = element.getAttribute("name");

if(!StringUtils.isEmpty(name)) id = name;

else id = beanClass.getName();

}

if (parserContext.getRegistry().containsBeanDefinition(id)) {

throw new IllegalStateException("Duplicate spring bean id " + id);

}

parserContext.getRegistry().registerBeanDefinition(id, beanDefinition);

String annotationPackage = element.getAttribute("package");

if(!StringUtils.isEmpty(annotationPackage))

beanDefinition.getPropertyValues().add("annotationPackage", annotationPackage);

return beanDefinition;

}

}

BeanDefinitionParser的应用参见Spring官方文档。

Bean注册工具类

package com.system.annotation.process;

import org.springframework.beans.BeansException;

import org.springframework.beans.factory.config.BeanDefinition;

import org.springframework.beans.factory.support.BeanDefinitionBuilder;

import org.springframework.beans.factory.support.BeanDefinitionRegistry;

import org.springframework.context.ApplicationContext;

import org.springframework.context.ApplicationContextAware;

import org.springframework.context.ConfigurableApplicationContext;

public class BeanRegistry implements ApplicationContextAware{

private ApplicationContext applicationContext;

private ConfigurableApplicationContext configurableApplicationContext;

public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {

this.applicationContext = applicationContext;

if(applicationContext instanceof ConfigurableApplicationContext){

this.configurableApplicationContext = (ConfigurableApplicationContext)this.applicationContext;

}

}

public BeanRegistry(){

}

public BeanRegistry(ApplicationContext applicationContext){

this.setApplicationContext(applicationContext);

}

public BeanDefinition register(Class> clazz){

if(configurableApplicationContext == null)return null;

BeanDefinitionRegistry beanDefinitonRegistry =

(BeanDefinitionRegistry)configurableApplicationContext.getBeanFactory();

BeanDefinitionBuilder beanDefinitionBuilder = this.createBuilder(clazz);

BeanDefinition beanDefinition = beanDefinitionBuilder.getRawBeanDefinition();

beanDefinitonRegistry.registerBeanDefinition(clazz.getName(),beanDefinition);

return beanDefinition;

}

private BeanDefinitionBuilder createBuilder(Class> clazz){

BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clazz);

return beanDefinitionBuilder;

}

}

处理@Endpoint

package com.system.annotation.process;

import com.system.annotation.custom.Endpoint;

import org.springframework.beans.BeansException;

import org.springframework.beans.factory.DisposableBean;

import org.springframework.beans.factory.config.BeanFactoryPostProcessor;

import org.springframework.beans.factory.config.BeanPostProcessor;

import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;

import org.springframework.beans.factory.support.BeanDefinitionRegistry;

import org.springframework.context.ApplicationContext;

import org.springframework.context.ApplicationContextAware;

import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;

import org.springframework.core.type.filter.AnnotationTypeFilter;

import org.springframework.util.StringUtils;

public class EndpointBeanProcessor implements

BeanFactoryPostProcessor, DisposableBean, BeanPostProcessor, ApplicationContextAware{

private final String COMMA_SPLIT_PATTERN = ",";

private ApplicationContext applicationContext;

private String annotationPackage;

private String[] annotationPackages;

private BeanRegistry beanRegistry;

public void setAnnotationPackage(String annotationPackage) {

this.annotationPackage = annotationPackage;

if (!StringUtils.isEmpty(this.annotationPackage))

this.annotationPackages = this.annotationPackage.split(this.COMMA_SPLIT_PATTERN);

}

@Override

public void setApplicationContext(ApplicationContext applicationContext)

throws BeansException {

System.out.println("setApplicationContext...");

this.applicationContext = applicationContext;

this.beanRegistry = new BeanRegistry(this.applicationContext);

}

/**

* 扫描{@link com.system.annotation.custom.Endpoint}注解

*/

@Override

public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

if (annotationPackage == null || annotationPackage.length() == 0) {

return;

}

if (beanFactory instanceof BeanDefinitionRegistry) {

BeanDefinitionRegistry beanDefinitionRegistry = (BeanDefinitionRegistry) beanFactory;

ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(beanDefinitionRegistry, true);

AnnotationTypeFilter filter = new AnnotationTypeFilter(Endpoint.class);

scanner.addIncludeFilter(filter);

scanner.scan(annotationPackages);

}

}

@Override

public Object postProcessBeforeInitialization(Object bean, String beanName)

throws BeansException {

return bean;

}

/**

* 实例化之后,打印扫描的类

*

*@param bean

*@param beanName

*@return

*@throws BeansException

*/

@Override

public Object postProcessAfterInitialization(Object bean, String beanName)

throws BeansException {

if (!this.isMatchPackage(bean)) return bean;

Endpoint endpoint = bean.getClass().getAnnotation(Endpoint.class);

if (endpoint != null) {

System.out.println(bean.getClass());

}

return bean;

}

@Override

public void destroy() throws Exception {

System.out.println("destroy...");

}

/**

* 包是否匹配

*

*@param bean

*@return

*/

private boolean isMatchPackage(Object bean) {

if (annotationPackages == null || annotationPackages.length == 0) {

return true;

}

String beanClassName = bean.getClass().getName();

for (String pkg : annotationPackages) {

if (beanClassName.startsWith(pkg)) {

return true;

}

}

return false;

}

}

这里已经实现了注解的扫描。然后需要在postProcessAfterInitialization方法中写业务处理代码。AfterInitialization表示Bean已经创建并且注入属性。

可以在spring容器实例化bean之后,在执行bean的初始化方法前后,添加一些自己的处理逻辑。

继承的几个类的大致介绍:

BeanFactoryPostProcessor: Spring允许BeanFactoryPostProcessor在容器实例化任何其它bean之前读取配置元数据,并可以根据需要进行修改,例如可以把bean的scope从singleton改为prototype,也可以把property的值给修改掉。可以同时配置多个BeanFactoryPostPro* cessor,并通过设置'order'属性来控制各个BeanFactoryPostProcessor的执行次序。

BeanPostProcessor: 可以在spring容器实例化bean之后,在执行bean的初始化方法前后,添加一些自己的处理逻辑。

DisposableBean : Spring提供了一些标志接口,用来改变BeanFactory中的Bean的行为,InitializingBean和DisposableBean。实现这些接口将会导致BeanFactory调用前一个接口的afterPropertiesSet()方法,调用后一个接口的destory()方法,从而使得bean可以在初始化和析构后做一些特定的动作。

ApplicationContextAware: 当一个类实现了这个接口(ApplicationContextAware)之后,这个类就可以方便获得ApplicationContext中的所有bean。换句话说,就是这个类可以直接获取spring配置文件中,所有有引用到的bean对象。

让Spring识别扩展

首先在classpath的META-INF下创建spring.handlers,内容如下

http\://www.orchome.com/schema/std/ws=com.system.annotation.AnnotationNamespaceHandler

在这个文件中指明了哪个命名空间需要哪个类来处理。

然后再创建spring.schemas

http\://www.orchome.com/schema/std/ws/stdws-1.0.xsd=META-INF/schema/stdws-1.0.xsd

指明了Sechma文件的位置,Spring会使用这里制定的xsd文件来验证配置是否正确。

测试

创建接口

package com.system.annotation.service;

import javax.jws.WebService;

@WebService

public interface HelloService{

public String syHi(String name);

}

实现类

package com.system.annotation.service;

import com.system.annotation.custom.Endpoint;

import javax.jws.WebService;

@Endpoint(address="HelloService", id = "HelloServiceEndpoint")

@WebService(endpointInterface= "com.system.annotation.service.HelloService")

public class HelloServiceImpl implements HelloService{

@Override

public String syHi(String name) {

return "Hello "+name;

}

}

测试用例

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration(locations={"classpath:applicationContext2.xml"})

public class InitializationTest{

@Test

public void test(){

}

}

在处理类中有一段代码是将有@Endpoint注解的类都打印出来,所以如果类名被打印出来就表示配置正常了。

运行测试用例,控制台能够看到

class com.codestd.spring.cxf.ws.HelloServiceImpl

本次扩展基本实现。

本此教程的内容可以在Spring官方文档第42章中找到。

项目结果如下:

├── README.md

├── pom.xml

├── spring-schema.iml

└── src

├── main

│ ├── java

│ │ └── com

│ │ └── system

│ │ └── annotation

│ │ ├── AnnotationBeanDefinitionParser.java

│ │ ├── AnnotationNamespaceHandler.java

│ │ ├── custom

│ │ │ └── Endpoint.java

│ │ ├── process

│ │ │ ├── BeanRegistry.java

│ │ │ └── EndpointBeanProcessor.java

│ │ └── service

│ │ ├── HelloService.java

│ │ ├── HelloServiceImpl.java

│ │ └── HelloServiceImpl2.java

│ └── resources

│ ├── META-INF

│ │ ├── schema

│ │ │ └── stdws-1.0.xsd

│ │ ├── spring.handlers

│ │ └── spring.schemas

│ └── SpringAnnotaionContext.xml

└── test

└── java

└── TestAnnotationSchema.java

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值