在JavaEE中使用CDI的简单面向方面的编程(AOP)

我们编写满足特定业务逻辑的服务API。 涵盖所有服务API(如安全性,日志记录,审核,度量延迟等)的跨领域问题很少。 这是一个重复的非业务代码,可以在其他方法之间重用。 重用的一种方法是将这些重复的代码移入其自己的方法,并在服务API中调用它们,例如:

public class MyService{
   public ServiceModel service1(){
      isAuthorized();
      //execute business logic.
   }
}

public class MyAnotherService{
  public ServiceModel service1(){
    isAuthorized():
    //execute business logic. 
  }
}

上面的方法会起作用,但不会在不产生代码噪声的情况下将交叉关注点与业务逻辑混合在一起。 有另一种方法可以通过使用Aspect来解决上述要求,这种方法称为面向方面的编程(AOP)。 您可以使用AOP的不同方式-通过使用Spring AOP,JavaEE AOP。 在此示例中,我将尝试在Java EE应用程序中使用使用CDI的AOP。 为了解释这一点,我选择了一个非常简单的示例,该示例构建一个Web应用程序以从Database中获取少量记录并显示在浏览器中。

创建数据访问层

表结构为:

create table people(
    id INT NOT NULL AUTO_INCREMENT, 
    name varchar(100) NOT NULL,
    place varchar(100),
    primary key(id));

让我们创建一个Model类来保存个人信息

package demo.model;
public class Person{
  private String id;
  private String name;
  private String place;
  public String getId(){ return id; } 
  public String setId(String id) { this.id = id;}
  public String getName(){ return name; } 
  public String setName(String name) { this.name = name;}
  public String getPlace(){ return place; } 
  public String setPlace(String place) { this.place = place;}
}

让我们创建一个公开两种方法的数据访问对象–

  1. 获取所有人的细节
  2. 获取给定ID的一个人的详细信息
package demo.dao;

import demo.common.DatabaseConnectionManager;
import demo.model.Person;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

public class PeopleDAO {

    public List<Person> getAllPeople() throws SQLException {
        String SQL = "SELECT * FROM people";
        Connection conn = DatabaseConnectionManager.getConnection();
        List<Person> people = new ArrayList<>();
        try (Statement statement = conn.createStatement();
                ResultSet rs = statement.executeQuery(SQL)) {
            while (rs.next()) {
                Person person = new Person();
                person.setId(rs.getString("id"));
                person.setName(rs.getString("name"));
                person.setPlace(rs.getString("place"));
                people.add(person);
            }
        }
        return people;
    }

    public Person getPerson(String id) throws SQLException {
        String SQL = "SELECT * FROM people WHERE id = ?";
        Connection conn = DatabaseConnectionManager.getConnection();
        try (PreparedStatement ps = conn.prepareStatement(SQL)) {
            ps.setString(1, id);
            try (ResultSet rs = ps.executeQuery()) {
                if (rs.next()) {
                    Person person = new Person();
                    person.setId(rs.getString("id"));
                    person.setName(rs.getString("name"));
                    person.setPlace(rs.getString("place"));
                    return person;
                }
            }
        }

        return null;
    }
}

您可以使用自己的方法来获取新的连接。 在上面的代码中,我创建了一个静态实用程序,该实用程序返回了相同的连接。

创建拦截器

创建拦截器涉及2个步骤:

  1. 创建Interceptor绑定,该绑定创建带@InterceptorBinding注释的注释,该注释用于绑定拦截器代码和需要拦截的目标代码。
  2. 创建一个用@Interceptor注释的类,其中包含拦截器代码。 它包含用@AroundInvoke注释的方法,不同的生命周期注释, @AroundTimeout等。

让我们通过名称@LatencyLogger创建一个拦截器绑定

package demo;

import java.lang.annotation.Target;
import java.lang.annotation.Retention;
import static java.lang.annotation.RetentionPolicy.*;
import static java.lang.annotation.ElementType.*;
import javax.interceptor.InterceptorBinding;

@InterceptorBinding
@Retention(RUNTIME)
@Target({METHOD, TYPE})
public @interface LatencyLogger {
    
}

现在我们需要创建Interceptor代码,该代码以@Interceptor注释,并以上面创建的Interceptor绑定进行注释,即@LatencyLogger

package demo;
import java.io.Serializable;
import javax.interceptor.AroundInvoke;
import javax.interceptor.Interceptor;
import javax.interceptor.InvocationContext;

@Interceptor
@LatencyLogger
public class LatencyLoggerInterceptor implements Serializable{
  
  @AroundInvoke
    public Object computeLatency(InvocationContext invocationCtx) throws Exception{
        long startTime = System.currentTimeMillis();
        //execute the intercepted method and store the return value
        Object returnValue = invocationCtx.proceed();
        long endTime = System.currentTimeMillis();
        System.out.println("Latency of " + invocationCtx.getMethod().getName() +": " + (endTime-startTime)+"ms");
        return returnValue;
        
    }
}

上面的代码中有两个有趣的事情:

  1. 使用@AroundInvoke
  2. 传递给方法的InvocationContext类型的参数

@AroundInvoke将方法指定为拦截器方法。 一个Interceptor类只能有一个带有此注释的方法。 每当目标方法被拦截时,其上下文都会传递给拦截器。 使用InvocationContext可以获取方法的详细信息,并将参数传递给该方法。

我们需要在WEB-INF / beans.xml文件中声明上述拦截器

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
       bean-discovery-mode="all">
    
    <interceptors>
        <class>demo.LatencyLoggerInterceptor</class>
    </interceptors>
</beans>

创建带有拦截器注释的服务API

我们已经创建了Interceptor绑定和被执行的拦截器。 现在让我们创建服务API,然后使用Interceptor绑定对其进行注释

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package demo.service;

import demo.LatencyLogger;
import demo.dao.PeopleDAO;
import demo.model.Person;
import java.sql.SQLException;
import java.util.List;
import javax.inject.Inject;

public class PeopleService {

  @Inject
  PeopleDAO peopleDAO;

  @LatencyLogger
  public List<Person> getAllPeople() throws SQLException {
    return peopleDAO.getAllPeople();
  }

  @LatencyLogger
  public Person getPerson(String id) throws SQLException {
    return peopleDAO.getPerson(id);
  }

}

我们已经用Interceptor绑定@LatencyLogger注释了服务方法。 另一种方法是在类级别进行注释,然后将注释应用于类的所有方法。 还要注意的另一件事是@Inject批注,该批注注入实例,即将依赖项注入到类中。

接下来是连接Controller和View以显示数据。 控制器是servlet,视图是使用JSTL标记的纯JSP。

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package demo;

import demo.model.Person;
import demo.service.PeopleService;
import java.io.IOException;
import java.sql.SQLException;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.inject.Inject;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet(name = "AOPDemo", urlPatterns = {"/AOPDemo"})
public class AOPDemoServlet extends HttpServlet {

  @Inject
  PeopleService peopleService;

  @Override
  public void doGet(HttpServletRequest request, HttpServletResponse response)
          throws ServletException, IOException {
    try {
      List<Person> people = peopleService.getAllPeople();
      Person person = peopleService.getPerson("2");
      request.setAttribute("people", people);
      request.setAttribute("person", person);
      getServletContext().getRequestDispatcher("/index.jsp").forward(request, response);
    } catch (SQLException ex) {
      Logger.getLogger(AOPDemoServlet.class.getName()).log(Level.SEVERE, null, ex);
    }
  }
}

上面的servlet可在http:// localhost:8080 /获得。

/ AOPDemo。 它获取数据并重定向到视图以显示该数据。 请注意,该服务也已使用@Inject注释注入。 如果没有注入依赖项,而是使用new创建依赖项,则拦截器将无法工作。 这是我在构建此样本时意识到的重要一点。

呈现数据的JSP将是

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" 
           uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>AOP Demo</title>
  </head>
  <body>
    <h1>Hello World!</h1>
    <table>
      <tr>
        <th>Id</th>
        <th>Name</th>
        <th>Place</th>
      </tr>
      <c:forEach items="${requestScope.people}" var="person">
        <tr>
          <td><c:out value="${person.id}"/></td>
          <td><c:out value="${person.name}"/></td>
          <td><c:out value="${person.place}"/></td>
        </tr>
      </c:forEach>
    </table>
    <br/>
    Details for person with id=2
    <c:out value="Name ${person.name} from ${person.place}" />
  </body>
</html>

这样,您将可以使用Interceptor构建一个非常简单的应用程序。 感谢您的阅读,并一直与我在一起。 请分享您的查询/反馈作为评论。 并在您的朋友之间分享这篇文章!

翻译自: https://www.javacodegeeks.com/2014/09/simple-aspect-oriented-programming-aop-using-cdi-in-javaee.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值