Tomcat JDNI
https://tomcat.apache.org/tomcat-8.5-doc/jndi-resources-howto.html
Introduction
Tomcat为在其下运行的每个web应用程序提供了一个JNDI InitialContext(class)实现实例,其方式与Java企业版应用服务器提供的方式兼容。
Java EE标准在/WEB-INF/web.xml文件中提供了一组标准元素来引用/定义资源。
web.xml configuration
以下元素可以在web应用程序的web应用程序部署描述符(/web-inf/web.xml)中用于定义资源
<env-entry>
- Environment entry, a single-value parameter that can be used to configure how the application will operate. (环境键值,一个单值参数,配置应用程序如何执行,感觉像是环境变量)<resource-ref>
- Resource reference, which is typically to an object factory for resources such as a JDBCDataSource
, a JavaMailSession
, or custom object factories configured into Tomcat. (资源引用,通常是个对象工厂,数据源,JavaMail或者自定义对象工厂配置<resource-env-ref>
- Resource environment reference, a new variation ofresource-ref
added in Servlet 2.4 that is simpler to configure for resources that do not require authentication information.(资源环境引用)
context.xml configuration
如果tomcat不能识别resource factory或者需要额外的配置。需要在 $CATALINA_BASE/conf/server.xml 或者每个项目的 (META-INF/context.xml
) 中进行配置
Tomcat specific resource configuration is performed using the following elements in the
<Context>
element:
<Environment>
: 相当于<env-entry>
<Resource>
:相当于<resource-ref>
<ResourceLink>
:资源链接,访问<Tranaction>
: 添加一个资源工厂,用于实例化UserTransaction对象实例,该实例可在java:comp/UserTransaction上使用。
如果一个资源已经在context.xml中定义,那么不需要在web.xml中进行定义。
建议在web.xml中进行定义,记录web程序的需要的资源。
- 如果相同的配置在
<Environment>
和<env-entry>
都有定义,只有在 中的override设置为true,web.xml中的<env-entry>
才会生效
Using resources
InitialContext
在web初始化部署时配置,并且对web应用只读访问。所有资源都放在 java:comp/env 下。
// Obtain our environment naming context
Context initCtx = new InitialContext();
Context envCtx = (Context) initCtx.lookup("java:comp/env");
// Look up our data source
DataSource ds = (DataSource)
envCtx.lookup("jdbc/EmployeeDB");
// Allocate and use a connection from the pool
Connection conn = ds.getConnection();
... use this connection to access the database ...
conn.close();
Tomcat Standard Resource Factories 标准资源工厂配置
Generic JavaBean Resources
这个资源工厂可以用来创建任何符合标准javabean命名约定的Java类的对象(例如,它有一个无参数构造函数,并且有符合setFoo()命名模式的属性设置器)。如果工厂的singleton属性设置为false,则资源工厂只会在每次对该条目进行lookup()时创建相应bean类的新实例。
- Create Your JavaBean Class
public class MyBean {
private String foo = "Default Foo";
public String getFoo() {
return (this.foo);
}
public void setFoo(String foo) {
this.foo = foo;
}
private int bar = 0;
public int getBar() {
return (this.bar);
}
public void setBar(int bar) {
this.bar = bar;
}
}
- 在 web.xml中定义
<resource-env-ref>
<description>
Object factory for MyBean instances.
</description>
<!-- JNDI获取的名称 -->
<resource-env-ref-name>
bean/MyBeanFactory
</resource-env-ref-name>
<!-- MyBean 的全路径名 -->
<resource-env-ref-type>
com.webapp.domain.MyBean
</resource-env-ref-type>
</resource-env-ref>
- 配置context.xml 的BeanFactory Resource
<?xml version="1.0" encoding="UTF-8"?>
<Context>
<Resource name="bean/MyBeanFactory" auth="Container"
type="com.webapp.domain.MyBean"
factory="org.apache.naming.factory.BeanFactory"
bar="23" foo="JNDI FOO"/>
</Context>
- JNDI 查找
try {
Context context = new InitialContext();
Context env = (Context) context.lookup("java:comp/env");
MyBean myBean = (MyBean) env.lookup("bean/MyBeanFactory");
System.out.println(myBean); // MyBean{foo='JNDI FOO', bar=23}
} catch (NamingException e) {
e.printStackTrace();
}
MyBean会在初始化时调用setter方法进行赋值
forceString
有些bean具有不能从字符串值自动转换为类型的属性。使用Tomcat BeanFactory设置这些属性将会失败,并出现一个NamingException。如果这些bean提供了从字符串值设置属性的方法,则可以配置Tomcat BeanFactory来使用这些方法。配置是通过forceString属性完成的。
对于String类型或原始类型或其关联的原始包装类的属性,不需要使用forceString。
- JavaBean
public class MyBean2 {
private InetAddress local = null;
public InetAddress getLocal() {
return local;
}
public void setLocal(InetAddress ip) {
local = ip;
}
// 通过String转换为InetAddress 类型
public void setLocal(String localHost) {
try {
local = InetAddress.getByName(localHost);
} catch (UnknownHostException ex) {
}
}
private InetAddress remote = null;
public InetAddress getRemote() {
return remote;
}
public void setRemote(InetAddress ip) {
remote = ip;
}
public void hostName(String remoteHost) {
try {
remote = InetAddress.getByName(remoteHost);
} catch (UnknownHostException ex) {
}
}
@Override
public String toString() {
return "MyBean2{" +
"local=" + local +
", remote=" + remote +
'}';
}
}
- context.xml
- 可以指定JavaBean中的方法名remote=hostName
<Context ...>
...
<Resource name="bean/MyBeanFactory2" auth="Container"
type="com.webapp.domain.MyBean2"
factory="org.apache.naming.factory.BeanFactory"
forceString="local,remote=hostName"
local="localhost"
remote="127.0.0.1"/>
...
</Context>
- JNDI
try {
Context context = new InitialContext();
Context env = (Context) context.lookup("java:comp/env");
MyBean myBean = (MyBean) env.lookup("bean/MyBeanFactory");
MyBean2 myBean2 = (MyBean2) env.lookup("bean/MyBeanFactory2");
System.out.println(myBean);
System.out.println(myBean2); // MyBean2{local=localhost/127.0.0.1, remote=/127.0.0.1}
} catch (NamingException e) {
e.printStackTrace();
}
JNDI配置数据源
- MySQL依赖
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.23</version>
</dependency>
- context.xml配置
<!-- 数据源配置 -->
<Resource name="jdbc/DataSource"
auth="Container"
type="javax.sql.DataSource"
username="root"
password="root"
driverClassName="com.mysql.cj.jdbc.Driver"
url="jdbc:mysql://localhost:3306/test?serverTimezone=Asia/Shanghai"
maxTotal="8"
maxIdle="4"/>
- JNDI
try {
DataSource dataSource = (DataSource) env.lookup("jdbc/DataSource");
Connection connection = dataSource.getConnection();
System.out.println(connection); // jdbc:mysql://localhost:3306/test?serverTimezone=Asia/Shanghai, UserName=root@localhost, MySQL Connector/J
} catch (Exception e) {
e.printStackTrace();
}
Adding Custom Resource Factories 自定义资源工厂
Write A Resource Factory Class
-
定义类实现 javax.naming.spi.ObjectFactory 接口,每次进行lookup时,会调用getObjectInstance方法
-
Object obj - ( 对于Tomcat,这将始终是一个类型对象 javax.naming.Reference )
-
Name name - 该工厂绑定到的相对于nameCtx的名称,如果没有指定名称,则为空。
-
Context nameCtx - 指定name参数的相对上下文,如果name相对于默认初始上下文,则为空。
-
Hashtable environment - 用于创建此对象的环境(可能为空)。这在Tomcat对象工厂中通常被忽略。
public class MyBeanFactory implements ObjectFactory {
@Override
public Object getObjectInstance(Object obj, Name name2, Context nameCtx, Hashtable<?, ?> environment) throws Exception {
// Acquire an instance of our specified bean class
MyBean bean = new MyBean();
// Customize the bean properties from our attributes
Reference ref = (Reference) obj;
Enumeration addrs = ref.getAll();
while (addrs.hasMoreElements()) {
RefAddr addr = (RefAddr) addrs.nextElement();
String name = addr.getType();
String value = (String) addr.getContent();
if (name.equals("foo")) {
bean.setFoo(value);
} else if (name.equals("bar")) {
try {
bean.setBar(Integer.parseInt(value));
} catch (NumberFormatException e) {
throw new NamingException("Invalid 'bar' value " + value);
}
}
}
// Return the customized instance
return (bean);
}
}
Declare Your Resource Requirements
<resource-env-ref>
<description>
Object factory for MyBean instances.
</description>
<resource-env-ref-name>
bean/MyBeanFactory3
</resource-env-ref-name>
<resource-env-ref-type>
com.webapp.domain.MyBean
</resource-env-ref-type>
</resource-env-ref>
Code Your Application’s Use Of This Resource
// 自定义资源工厂
MyBean myBean3 = (MyBean) env.lookup("bean/MyBeanFactory3");
System.out.println(myBean3);
Configure Tomcat’s Resource Factory
<!-- 自定义资源工厂 -->
<Resource name="bean/MyBeanFactory3" auth="Container"
type="com.webapp.domain.MyBean"
factory="com.webapp.factory.MyBeanFactory"
singleton="false"
bar="23"/>