Shiro JNDI
概念
Shiro的JNDI主要是用来为JdbcRealm提供DataSource的。话说天下文章一大抄,shiro抄袭Spring的又一力证就是关于JNDI的设计。
Tomcat配置JDNI,Shiro获得数据源的Demo
${catalina.base}/conf/context.xml中配置Resource
<Resource name="Iss002DataSource" type="org.apache.commons.dbcp.BasicDataSource" maxActive="100" maxIdle="30" maxWait="10000" username="iss002" password="iss002password" driverClassName="oracle.jdbc.driver.OracleDriver" url="jdbc:oracle:thin:@127.0.0.1:1521:iss002" />
Shiro的API获得DataSource
package com.wjz.demo.jndi; import javax.sql.DataSource; import org.apache.shiro.jndi.JndiObjectFactory; public class ShiroJndiDemo { public static void main(String[] args) { JndiObjectFactory<DataSource> factory = new JndiObjectFactory<DataSource>(); factory.setResourceName("Iss002DataSource"); factory.setRequiredType(DataSource.class); factory.setResourceRef(true); DataSource dataSource = factory.getInstance(); System.out.println(dataSource.getClass().getName()); } }
JndiObjectFactory
工厂模式,根据注入的resourceName和requiredType获得实例
requiredType可以不注入,获得实例时找定位器开始定位
public T getInstance() { try { if(requiredType != null) { return requiredType.cast(this.lookup(resourceName, requiredType)); } else { return (T) this.lookup(resourceName); } } catch (NamingException e) { final String typeName = requiredType != null ? requiredType.getName() : "object"; throw new IllegalStateException("Unable to look up " + typeName + " with jndi name '" + resourceName + "'.", e); } }
JndiLocator
具备了JndiTempleate模板器
protected Object lookup(String jndiName, Class requiredType) throws NamingException { if (jndiName == null) { throw new IllegalArgumentException("jndiName argument must not be null"); } String convertedName = convertJndiName(jndiName); Object jndiObject; try { // 使用JndiTemplate模板器定位jndiObject jndiObject = getJndiTemplate().lookup(convertedName, requiredType); } catch (NamingException ex) { if (!convertedName.equals(jndiName)) { // Try fallback to originally specified name... if (log.isDebugEnabled()) { log.debug("Converted JNDI name [" + convertedName + "] not found - trying original name [" + jndiName + "]. " + ex); } jndiObject = getJndiTemplate().lookup(jndiName, requiredType); } else { throw ex; } } log.debug("Located object with JNDI name '{}'", convertedName); return jndiObject; } // 因为jndiName必须有前缀'java:comp/env/',该方法为注入的name添加前缀 protected String convertJndiName(String jndiName) { // Prepend container prefix if not already specified and no other scheme given. if (isResourceRef() && !jndiName.startsWith(CONTAINER_PREFIX) && jndiName.indexOf(':') == -1) { jndiName = CONTAINER_PREFIX + jndiName; } return jndiName; }
JndiTemplate
获得Jndi对象使用了javax.naming.*的API,获得过程都是相同的使用了模板模式,执行方法中使用了回调模式
public Object lookup(final String name) throws NamingException { log.debug("Looking up JNDI object with name '{}'", name); // 类似于异步调用一样,执行了execute方法,又返回来调用了JndiCallBack的行为 // 当然可以创建了InitialContext然后执行lookup再关闭context // execute方法传入一个回调,执行过程中创建了InitialContext,回调获得jndiObject再回来关掉context return execute(new JndiCallback() { public Object doInContext(Context ctx) throws NamingException { Object located = ctx.lookup(name); if (located == null) { throw new NameNotFoundException( "JNDI object with [" + name + "] not found: JNDI implementation returned null"); } return located; } }); } public Object execute(JndiCallback contextCallback) throws NamingException { Context ctx = createInitialContext(); try { return contextCallback.doInContext(ctx); } finally { try { ctx.close(); } catch (NamingException ex) { log.debug("Could not close JNDI InitialContext", ex); } } } protected Context createInitialContext() throws NamingException { Properties env = getEnvironment(); Hashtable icEnv = null; if (env != null) { icEnv = new Hashtable(env.size()); for (Enumeration en = env.propertyNames(); en.hasMoreElements();) { String key = (String) en.nextElement(); icEnv.put(key, env.getProperty(key)); } } return new InitialContext(icEnv); }
JndiCallback
定义公共行为,在javax.naming.Context中查找JndiObject