閱讀757 返回首頁    go 阿裏雲 go 技術社區[雲棲]


一篇文章學會spring boot(包括jms和hessian的集成)

之前在學習spring cloud微服務的時候,由於spring cloud的基礎是spring boot,因此曾簡單地了解過spring boot,但也隻是簡單的了解過而已。
而現在,需要把struts2項目改為spring boot,一開始時以為是整個項目重構,不僅限於struts2部分,因此就相對更係統、更細致的學了一下spring boot。
整個過程由易到難,大概分成了這麼些模塊:

一、創建簡單的spring boot web項目
很多時候學一個新的東西,都需要從最簡單的地方開始,然後讓自己看到成功後的效果,這樣才能更有信心繼續下去,至於這第一步,我在寫spring cloud相關學習記錄的時候,第一篇就是spring boot,因此便不需要再寫一遍,那一篇博客是:
springcloud微服務一:spring boot基礎項目搭建及問題處理

二、配置文件加載類
雖然說spring boot原則上是建議去掉xml配置文件,但是對於一個已經成型並上線運行著的項目而言,不論是代碼還是配置都是非常龐大而且複雜的。
這些配置文件不隻是spring的、struts2的,還有spring和mq集成的,還有spring和hessian集成的等等。
僅涉及到spring和struts2的配置文件比較好改,而spring和mq以及hessian集成的就稍微有些麻煩。
因此在一開始我心中便有了一個設想,就是mq集成和hessian集成的部分先還是以配置文件的方式不變,等簡單易改的部分搞定後再說,於是便需要知道在沒有了web.xml文件的spring boot項目中,我需要如何引入加載這些暫時不能去掉的xml文件。
經了解之後發現其實還是很簡單的,隻需要自己再寫一個空的java類,然後使用兩個注解就好:

@Configuration
@ImportResource(value = "classpath:spring.xml")
public class ConfigClass {

}

@Configuration是向spring聲明這是個配置類,@ImportResource則是指明需要引入的配置文件路徑。

三、使用注解給屬性賦值
在過去的項目中,有一些數據存放在properties文件中,然後在spring的xml文件中引用,從而給特定的對象的屬性賦值。
而如果整個項目改成spring boot,就需要盡可能去掉能去掉的xml配置文件,就沒有了這種注入賦值,所以還需要知道怎樣在spring boot中達到xml文件配置一樣的賦值效果。
經了解後發現也是比較簡單地,隻需要在對應的屬性上使用@Value注解:

public class User {
    @Value("${tuser.name}")
    private String name;
    @Value("${tuser.age}")
}

當然了,這種寫法的前提是在properties文件中有相應的屬性配置,例如:

tuser.name=testtttttt
tuser.age=123

並且spring boot項目啟動後能夠加載到這個properties文件,spring boot默認會加載resources目錄下的application.properties文件,如果我們這些配置不在這個文件中,還需要進行其他的配置,例如在application.properties文件中使用spring.profiles.active進行指定。

四、使用注解給對象賦值:
雖然說在java中有一切皆對象的說法,屬性和屬性所屬對象都統稱對象,但是這裏為了區分,我隻能在上邊更精確的說到屬性。
而上邊那種賦值方式有個很明顯的缺陷,就是每個屬性都需要使用@Value注解,如果這個類恰好很大,有幾十個屬性,那麼這種賦值將是一個很讓人煩躁的工作。
而spring boot中也正好提供了一個更加方便的方式,隻需要一個注解,就可以引用配置文件中的內容給整個對象賦值。
例如配置文件中有如下內容:

mytest:
  user:
    name: test
    age: 22
    phone: 13533559797

上邊這種寫法是yaml文件的寫法,也就是在spring boot中默認識別properties類型的配置文件和yaml的配置文件。
上邊的寫法實際上等同於properties文件中如下的寫法:

mytest.user.name=test
mytest.user.age=22
mytest.user.phone=13533559797

而我們要把這樣一些配置直接賦值給特定對象的方式,就是使用@ConfigurationProperties注解:

@ConfigurationProperties(prefix = "mytest.user")
public class User {

    // @Value("${test.user.name}")
    private String name;
    // @Value("${age}")
    private int age;
    // @Value("${phone}")
    private String phone;
}

類上邊的那個注解等同於下邊被注釋掉的三個注解,當然了,不論哪種方式,實現的前提都是這個user類能夠被spring 掃描到,也就是說能夠被spring所管理。

五、filter過濾器
在原本的spring web項目中,web.xml文件是必不可少的,啟動tomcat後就會來加載這個文件。
通常我們都會在這個文件中配置一些servlet以及過濾器,例如字符集過濾器,而spring boot不再使用web.xml,就需要我們能夠用其他方式解決原本web.xml中的過濾器的問題。
這裏寫一個過濾器也很簡單,要實現javax.servlet.Filter接口,並借助幾個注解,隻不過這個功能應該是java自己的,而不是spring boot新提供的:

/**
*@auth tuzongxun
*/
@WebFilter(filterName = "myFilter", urlPatterns = "/*")
public class MyFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("進入filter");

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException {
        chain.doFilter(request, response);

    }

    @Override
    public void destroy() {
        System.out.println("走出filter");

    }

}

六、listener監聽器
說到了過濾器,接下來自然要說的就是監聽器,在java web項目中,過濾器和監聽器都用的很多,也都常常在web.xml文件中有相應的配置。
在spring boot中寫一個監聽器就更加簡單,比如就僅僅是下邊的幾行代碼就夠了:

@WebListener
public class MyListener implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("contextlistener init");

    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("contextlistener destroy");

    }

}

同樣的,上邊的類所實現的接口以及相應的注解也都不是spring boot的,而是javax.servlet的。
上邊例子中我們寫的是一個contextListener,同樣的也可以寫一個requestListener以及其他的listener,例如:

@WebListener
public class MyListener1 implements ServletRequestListener {

    @Override
    public void requestDestroyed(ServletRequestEvent sre) {
        System.out.println("requestlistener destroy");

    }

    @Override
    public void requestInitialized(ServletRequestEvent sre) {
        System.out.println("requestlistener init");

    }

}

七、ERROR錯誤頁麵
在web.xml中除了配置了filter和listener外,比較常見的就還有配置error錯誤頁麵,我們項目中也同樣這麼做了,因此在拋棄web.xml的時候,也必然要考慮error頁麵的問題,那麼在spring boot中也同樣是非常的簡單,比如寫這樣一個類:

@Component
public class Test {
@Bean
public EmbeddedServletContainerCustomizer containerCustomizer() {
return new EmbeddedServletContainerCustomizer() {
@Override
public void customize(ConfigurableEmbeddedServletContainer container) {
  ErrorPage error404Page = new ErrorPage(HttpStatus.NOT_FOUND, "/404.html");
  ErrorPage error500Page = new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/500.html");
  container.addErrorPages(error404Page, error500Page);
            }
        };
    }
}

需要注意的是,我這種寫法是jdk1.6可用的,還有一種寫法需要jdk1.7以上版本才行,這裏就不舉例了。
並且我這裏是寫了一個新的類,我們也可以直接把具體的方法寫到啟動類那裏,效果是一樣的,隻要都能被spring 掃描到。

八、定時任務
原本項目中使用了quartz定時任務,使用了大量的xml配置文件,這裏就使用schedule替代。
不過,這一條雖然列在了這裏,但是實際上我覺得可以不用列出來,因為這裏使用schedule代替quartz,實際上也是spring原本就有的,並不是有了spring boot之後才出現,具體的我也曾寫過相應的博客,例如:
spring schedule定時任務(一):注解的方式
隻不過,在spring boot中更加的簡單,不需要我們再做任何的動作,隻需要這個簡單的類和注解就ok了:

@Component
@EnableScheduling
public class MySchedule {

    @Scheduled(cron = "*/2 * * * * ?")
    public void sTest() {
        System.out.println(Calendar.getInstance().getTime());
    }

}

九、返回頁麵
返回頁麵,在spring boot中叫引用模板,需要特定的依賴,例如:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

當然了,這隻是其中一種。
引入了相應的依賴後,我們在spring boot項目的resources目錄下的templates目錄下建立具體的html頁麵,然後具體的controller中隻需要返回相應的html的文件名,即能成功返回頁麵。
例如templates下有index.html文件,在controller中就可以寫成這樣:

@RequestMapping("/index")
public String index(ModelMap map) {
        map.put("name", "testfsdfdsfdsfsdf");
        return "index";
}

不過這裏我覺得應該也可以使用modelandview,但並沒有嚐試,不知是否可行,而且即便可行,在性能上如何也還未曾測試。

十、spring boot 集成activemq,即jms
前文中我有說到我們原本的項目中使用了spring 整合activemq,那麼改為spring boot後不可能就把activemq扔掉,還需要找到可行的集成方案,可喜的是,spring boot中有jms專門集成activemq。
要實現這種集成,我們首先還需要導入jms相應的依賴,創建項目的時候選擇jms,會在pom.xml文件中生成如下內容:

<dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-activemq</artifactId>
</dependency>

使用activemq發送消息需要有消息隊列,因此接下來就需要我們生成一個消息隊列的配置類,用來指定queue:

@Configuration
@EnableJms
public class ActiveMQConfig {
    @Bean // 配置一個消息隊列
    public Queue queue() {
        return new ActiveMQQueue("sample.queue");
    }
}

有了隊列以後,然後就是發送消息:

@Service
public class MQProduceService {
    @Autowired
    private JmsMessagingTemplate jmsMessagingTemplate;

    @Autowired
    private Queue queue;

    public void send(String msg) {// 向指定隊列中發送消息
        this.jmsMessagingTemplate.convertAndSend(this.queue, msg);
    }
}

就這樣簡單兩步,我們的activemq消息服務器就算是寫好了,如果再其他地方調用這個service中對應的方法,就可以實現消息的發送。
有了發送端,還需要有接受端,或者說消費端。也是需要先配置隊列,因為我這裏都寫在一個項目中,所以就公用同一個配置就好,具體的消費端代碼如下:

@Service
public class MQConsumerService {

    private String text;

    @JmsListener(destination = "sample.queue") // 監聽指定消息隊列
    public void receiveQueue(String text) {
        this.text = text;
        System.out.println(text);
    }

    public String receive() {
        return text;
    }
}

不論是發送端還是消費端,代碼都很簡單,為了更直觀的看到結果,我編寫了一個controller來調用相應的服務實現發送和接受,代碼如下:

@RestController
public class MQController {

    @Autowired
    private MQProduceService produceService;

    @Autowired
    private MQConsumerService consumerService;

    @RequestMapping("/send")
    public String send() {
        produceService.send("this is an activemq message");
        return "send";
    }

    @RequestMapping("/receive")
    public String receive() {
        String str = consumerService.receive();
        return str;
    }
}

這個代碼非常的常規,也就不做多的解釋了。

十一、spring boot中使用hessian
mq分為發送端和消費端,而hessian作為一種webservice,也是一樣具有服務端和消費端,不過與mq不同的是,hessian需要在web.xml進行一定的配置。
與之前hessian的實現方式一樣的是,服務端需要有相應的接口類和實現類,例如:

public interface HelloWorld {
    public String hello();
    public Map<String, String> helloMap();
}

@Service("helloWorld")
public class HelloWorldImpl implements HelloWorld {

    private static final long serialVersionUID = 1L;

    @Override
    public String hello() {
        return "hello hessian";
    }

    @Override
    public Map<String, String> helloMap() {
        Map<String, String> map = new HashMap<String, String>();
        map.put("message", "hello word!!!!!");
        return map;
    }

}

而不同的是,之前使用web.xml時在web.xml中進行了配置,而在這裏則是使用了一個類來暴露接口:

@Component
public class HService {
    @Autowired
    private HelloWorld helloWorld;

    @Bean(name = "/springBootDemo/HelloService")
    public HessianServiceExporter exportHelloService() {
        HessianServiceExporter exporter = new HessianServiceExporter();
        exporter.setService(helloWorld);
        exporter.setServiceInterface(HelloWorld.class);
        return exporter;
    }
}

就這樣,在不使用web.xml的情況下我們也一樣實現了spring boot和hessian服務端的集成,或者更準確的說,是spring和hessian服務端的集成。
而至於客戶端的集成,目前我還沒有整出可行的方案,因此便隻能繼續先使用之前xml文件配置的方式,引入這個xml文件然後進行後續的操作,具體的不用xml的方式還需要進一步探索,如果有朋友用過,歡迎指點和交流!

最後更新:2017-09-12 13:32:35

  上一篇:go  struts2改spring boot過程中一些問題及解決辦法記錄
  下一篇:go  jdk1.6環境下struts2改spring boot方案